ToplingDB 的序列化框架:最佳化到極致
ToplingDB的 分散式 Compact中 Client-Server 互動,使用了 topling-zip中的序列化框架,該序列化框架初版完成於 2006 年,後來命名為 febird 庫在 google code 上開源,再後來 google code 停止服務,febird 遷移到 github,有段時間重新命名為 nark,之後重新命名為 terark,目前 topling-zip 中程式碼的 namespace 仍是 terark。從 2006 年至今,除 namespace 名稱之外,該序列化框架的介面一直保持穩定,2016 年的時候,針對 C++11 進行了模板推導相關的大幅最佳化,但仍保持了介面的穩定。以下為原文正文,排版有輕微改動。
原文: febird.dataio 最佳化技術
作者: rockeet 發表日期: 2009年04月04日
分類: C++序列化 評論: 0 條 閱讀次數: 2,111 次
最佳化技術主要有:
(一)最佳化的 inline
1.1 頻繁呼叫的函式都使用inline
但是值得注意的是,在inline的時候,只inline最頻繁的分支,很少走到的分支使用非inline函式,例如:
void InputBuffer::ensureRead(void* vbuf, size_t length) {
// 為了效率,這麼實現可以讓編譯器更好地inline這個函式 // inline 後的函式體並儘可能小 if (m_cur+length <= m_end) {
memcpy(vbuf, m_cur, length);
m_cur += length;
} else
fill_and_ensureRead(vbuf, length);}
一般情況下,如果length是個不大的常數值,編譯器會把memcpy最佳化成賦值語句。至少在 VC2008 中我觀察到了這個最佳化。
但是這裡仍有一種不太最佳化的情況,在理想的情況下,編譯器應該把 m_cur/m_end 都放在暫存器中,只有在暫存器 Spill Out 的時候,才把它們的值從暫存器拷到物件,並呼叫 fill_and_ensureRead。但實際上編譯器沒有這麼做,每次都存記憶體讀取m_cur/m_end。這可能是編譯器觀察到 InputBuffer有點大,並且有虛擬函式。
1.2 MinMemIO/MemIO/AutoGrowMemIO
這個幾個效率更高,但只能在記憶體中操作,編譯器的極端最佳化,在這裡得到了體現:在Buffer類中,編譯器沒有做到我想要的最佳化,但是在這裡,編譯器做到了,他吧 MinMemIO 放到了暫存器中。
(二)拋棄標準 C++ stream,使用簡單、直接的 Stream/Buffer
2.1 可以對各種流進行快速緩衝的StreamBuffer,包括
i. 效率高、最常用的:InputBuffer/OutputBuffer
ii. 效率高、不常用的:SeekableInputBuffer/SeekableOutputBuffer
iii. 效率稍差、不常用的:SeekableBuffer,可讀也可寫,共享一個位置指標
iv. 這幾個Buffer結構簡單,操作直接,結合編譯器inline可以達到很高的效率,同時可以和實際Stream互操作。
(三)使用 typetraits 識別可以 memcpy 的類,進一步最佳化
a) 基本型別都可以進行 memcpy,並且這個 memcpy 實際上被最佳化成了賦值
b) 對稍微複雜的型別,有兩種方法:
i. 直接dump,不管它的格式
實現簡單,只管dump就行,boost::archive::binary_xxx實現了這種最佳化,但是它只能對基本型別和使用者宣告為可直接dump的類最佳化。並且如果febird也使用這種最佳化,將不能對Portable格式最佳化。
ii. 直接dump,再轉化格式
就比較複雜,需要一些技巧,febird做到了一點,不管對Native還是Portable格式,都做到了最佳化。因為序列化使用宏來進行宣告,因此,應用程式碼不用改變,只要認真最佳化這個宏,就可以做到。febird使用了這樣的技巧:
DATA_IO_LOAD_SAVE(MyData1, &a&b&c&d&e&f&g&h)
在這個宏呼叫中第二個引數
&a&b&c&d&e&f&g&h
被使用了多次,其中有一次展開後將是是這樣的:
DataIO_load_vector_impl(dio, *this,
DataIO_is_realdump<DataIO,0,true>()&a&b&c&d&e&f&g&h,
bswap)
其中高亮部分
DataIO_is_realdump<DataIO,0,true>()&a&b&c&d&e&f&g&h
將推匯出一個類
DataIO_is_realdump<DataIO, Size, IsDumpable>
,其中 Size 是 abcdefgh 的尺寸之和,IsDumpable 是abcdefgh 的 IsDumpable 的
and
結果,DataIO_load_vector_impl 以這個類為引數,進行函式呼叫的自動分派,如果
Size==sizeof(MyData1)
就說明 MyData 中沒有編譯器為對齊成員自動產生的 Padding,如果IsDumpable 同時為 true,那麼這個類就可以被 dump。但是這裡仍然有一個潛在的危險:
如果 &a&b&c&d&e&f&g&h 的順序和它們在 類定義中出現的順序不同,並且這個不同是有意為之,而不是粗心大意(雖然這個可能性很低,但不代表不存在),那麼這個最佳化產生的行為將 違背呼叫者的真實意圖。關於這一點,無法進行自動檢查,因此使用者需要 特別注意。如果 要測試是否出現了這種錯誤,可以先禁用這種最佳化,產生資料,然後使用最佳化,來讀取資料,如果資料格式不同,就說明出了錯。
(四)效果
使用了這麼多最佳化, 達到的效果,平均情況下,如果是基本型別 vector,比 boost 快不了太多,但是對複雜型別,比 boost 要 快20~50倍,如果資料已經過驗證,不用擔心越界,讀取時可以使用NativeDataInput<MinMemIO>,此時速度更加驚人: 比 boost 快 1600 倍!
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70013015/viewspace-2924755/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- ToplingDB 的序列化框架:簡介框架
- 將ipad發揮到極致iPad
- 3倍+提升,高德地圖極致效能最佳化之路地圖
- 極致效能最佳化:前端SSR渲染利器Qwik.js前端JS
- .NET8極致效能最佳化Non-GC HeapGC
- React內部的效能優化沒有達到極致?React優化
- 極致簡潔的微前端框架-京東MicroApp開源了前端框架APP
- 如何將 HTML5 效能發揮到極致HTML
- webpack + react 前端工程化實踐和暫不極致最佳化WebReact前端
- 牢記將iPhone特色硬體優勢發揮到極致iPhone
- 使用 Apache Fury 實現極快的序列化Apache
- SAE 的極致應用部署效率
- 將MySQL去重操作優化到極致之三彈連發MySql優化
- 思邁特軟體Smartbi:專注BI,把產品打造到極致
- 將Objective-C語法簡化到極致,從此不再煩惱!!!Object
- 把前端監控做到極致前端
- 阿里資料庫的極致彈性之路阿里資料庫
- React 同構與極致的效能優化React優化
- 從 350ms 到 80ms,打造 iOS 短影片的極致絲滑體驗iOS
- 極其精簡的PHP框架WJWPHP框架
- Protocol Buffer序列化Java框架-ProtostuffProtocolJava框架
- 用.NET框架實現序列化框架
- 迴圈引用導致的json序列化失敗JSON
- 深入理解RPC框架的序列化方案RPC框架
- 從LocalDateTime序列化探討全域性一致性序列化LDA
- 從 350ms 到 80ms,打造 iOS 短視訊的極致絲滑體驗iOS
- 程式設計師的美:極致與瘋狂程式設計師
- 用C++優雅的實現物件到檔案的序列化/反序列化C++物件
- 使用智慧最佳化器提高Oracle的效能極限Oracle
- 簡陋到極致便成了經典,看似很Low的開羅遊戲其實並不簡單遊戲
- 程式設計師修仙之路--把使用者訪問記錄優化到極致程式設計師優化
- 作為一個產品運營,如何利用資料把產品打造到極致?
- webpack4 的30個步驟打造優化到極致的 react 開發環境,如約而至Web優化React開發環境
- 蝦扯蛋之條件判斷的極致優化優化
- 分庫分表如何進行極致的優化優化
- Serializer 序列化 -----檢視層傳入一個變數到序列化器的方法變數
- 打造手淘極簡包的輕量化框架框架
- 簡易RPC框架:序列化機制RPC框架