既然編譯器可以判斷一個函式是否適合 inline,那還有必要自己加 inline 關鍵字嗎?
連結:
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
編譯器的 inline 是為了讓程式設計師與編譯器互相配合,改善效能的機制,編譯器確實可以判斷函式是否適合 inline,最簡單的例如 getter/setter。然而,inline 是否比不 inline 更好,這中間是有模糊地帶的,這就需要一些規則和閾值。對於現代的編譯器,我們主動加了 inline,編譯器大概只會更改那些 inline 規則中的相應閾值。
然而,實際上在很多時候,我們可以精心地編碼,讓編譯器能更好地進行 inline,讓 inline 的效果更好。我這裡有一個典型的範例:
的 中 Client-Server 互動,使用了 中的序列化框架,該序列化框架初版完成於 2006 年,後來命名為 febird 庫在 google code 上開源,再後來 google code 停止服務,febird 遷移到 github,有段時間重新命名為 nark,之後重新命名為 ,目前 topling-zip 中程式碼的 namespace 仍是 terark。從 2006 年至今,除 namespace 名稱之外,該序列化框架的介面一直保持穩定,2016 年的時候,針對 C++11 進行了模板推導相關的大幅最佳化,但仍保持了介面的穩定。以下為原文正文,排版有輕微改動。
原文:
作者: 發表日期: 2009年04月04日
分類: 評論: 條 閱讀次數: 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
這個幾個效率更高,但只能在記憶體中操作,編譯器的極端最佳化,在這裡得到了體現:在 中,編譯器沒有做到我想要的最佳化,但是在這裡,編譯器做到了,他吧 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-2924946/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C++ inline和constexpr函式可以多次定義問題C++inline函式
- 怎麼判斷自己是否適合學習六西格瑪?
- 【知識點】inline函式、回撥函式、普通函式inline函式
- JavaScript 判斷函式是否存在JavaScript函式
- 判斷一個有向圖是否有環
- bash函式應用之:判斷函式是否存在函式
- inline、block、inline-block這三個屬性值有什麼區別?inlineBloC
- 如何判斷豐田生產系統是否適合自己的企業?
- 內聯(inline)函式與虛擬函式(virtual)的討論inline函式
- 有必要參加SEO培訓嗎?自學可以嗎?
- JavaScript 判斷變數是否是函式JavaScript變數函式
- [譯]Effective Kotlin系列之探索高階函式中inline修飾符(三)Kotlin函式inline
- 現在加入Web前端學習還有市場嗎?自己是否適合學習前端Web前端
- 判斷欄位中是否有漢字
- 判斷一個物件是否為空物件,判斷一個物件中是否有空值物件
- 對一個連結串列判斷是否有環
- 多個陣列,判斷鍵值是否存在從屬關係陣列
- 正規表示式判斷是否是數字
- Inline行內函數簡單理解inline函數
- [譯]Kotlin中內聯類(inline class)完全解析(一)Kotlininline
- 【每日一題】 3232. 判斷是否可以贏得數字遊戲每日一題遊戲
- gnu inline asminlineASM
- 如何判斷一個玩法是否合格?
- 學習Python是否真的要參加培訓?真的有必要嗎?Python
- C#判斷一個字串是否是數字或者含有某個數字C#字串
- [譯] Swift 裡的強制 @inline 註解Swiftinline
- Solidity之旅(十八)內聯彙編 [inline assembly]Solidinline
- 如何判斷一個物件是否為空?物件
- 判斷一個陣列是否排好序陣列
- inline、const、mutable、this、staticinline
- 如何判斷一個雜湊函式的好壞函式
- 如何判斷一項技術是否有前途?
- MySQL函式-條件判斷函式MySql函式
- Python 操作 Excel,總有一個模組適合自己PythonExcel
- 判斷字串是否唯一字串
- jquery如何判斷是否按下了Enter鍵jQuery
- 如何判斷一個元素文字是否換行?
- python如何判斷一個物件是否是列表Python物件