《Efficient C++ : Performance Programming Techniques》是一本關(guān)于提高C++性能的編程書锌钮,書中的第八章到第十章探究了內(nèi)聯(lián)的技巧喇伯、利弊和使用場景攒砖,以下是對該內(nèi)容的讀書筆記。
內(nèi)聯(lián)的利與弊
內(nèi)聯(lián)的優(yōu)勢蛔琅,可以從函數(shù)調(diào)用的開銷去分析汽久。
函數(shù)調(diào)用需要開辟棧空間呛每,以及修改各種寄存器值以“保存現(xiàn)場”和保證函數(shù)的執(zhí)行。例如指令寄存器保存舊值覆蓋新值坡氯,棧指針寄存器記錄當前棧位置晨横,等等。所以函數(shù)即便執(zhí)行的代碼只有幾行箫柳,也會產(chǎn)生固定的開銷手形。
方法調(diào)用需要跳轉(zhuǎn)。頻繁的跳轉(zhuǎn)意味著cpu的大量時間都用在重定向指令上悯恍。
編譯器無法跨越函數(shù)邊界去做優(yōu)化库糠。例如
int x = 10; x = 20;
這樣的代碼,編譯器通常會將兩行合并為一行int x = 20;
涮毫。但是如果 x 作為函數(shù)的引用類型參數(shù)在函數(shù)體內(nèi)被賦值瞬欧,編譯器是無法做優(yōu)化的。
內(nèi)聯(lián)的弊端罢防,則是在于代碼膨脹以及由此造成的緩存命中問題和缺頁中斷艘虎。如果是函數(shù)調(diào)用,不同的調(diào)用執(zhí)行的都是同一地址的程序咒吐,如果緩存中有該段地址就可以快速執(zhí)行野建,而內(nèi)聯(lián)則是不同地址,會增大緩存空間恬叹,使得緩存命中率下降候生。而頁面錯誤也與此類似,試想如果內(nèi)聯(lián)函數(shù)代碼膨脹嚴重绽昼,就會頻繁發(fā)生缺頁中斷唯鸭,使得性能下降。
還有一種情況不應(yīng)該內(nèi)聯(lián)硅确,那就是工程中可能會頻繁變動的函數(shù)肿孵,因為內(nèi)聯(lián)會在實現(xiàn)層面和接口層面產(chǎn)生編譯依賴,一旦內(nèi)聯(lián)函數(shù)有所改動疏魏,所有調(diào)用該內(nèi)聯(lián)函數(shù)的模塊都要重新編譯停做。
有兩條內(nèi)聯(lián)規(guī)則,用來判斷什么情況下函數(shù)應(yīng)該內(nèi)聯(lián):
- singleton 唯一化大莫,即函數(shù)在程序中的調(diào)用點唯一蛉腌;
- trival 精簡化,即函數(shù)較短小,通常5行以內(nèi)的源碼烙丛,編譯后10條以內(nèi)的匯編指令舅巷。
虛函數(shù)內(nèi)聯(lián)
內(nèi)聯(lián)是編譯時的優(yōu)化,虛函數(shù)是運行時的機制河咽,虛函數(shù)如何能夠內(nèi)聯(lián)呢钠右?對于一個優(yōu)秀的編譯器,如果對象指針在初始化后賦值之后沒有再被賦值過忘蟹,那么該指針的虛函數(shù)調(diào)用其實在編譯期就可以確定對象類型的飒房,所以沒有必要用到多態(tài)。
條件內(nèi)聯(lián) 與 選擇性內(nèi)聯(lián)
C++的內(nèi)聯(lián)機制缺少機動性媚值,我們用條件內(nèi)聯(lián)和選擇性內(nèi)聯(lián)狠毯,來決定在不同場景下使用不同的內(nèi)聯(lián)策略。
條件內(nèi)聯(lián)將內(nèi)聯(lián)函數(shù)定義在.inl文件中褥芒,并通過預(yù)處理機制來控制.h文件是否include .inl文件嚼松。在include代碼前用一個宏INLINE來判斷是否include,在編譯選項時使用-D定義宏INLINE就可以打開內(nèi)聯(lián)開關(guān)锰扶。
條件內(nèi)聯(lián)仍然不夠靈活献酗,要么全部內(nèi)聯(lián),要不都不坷牛。選擇性內(nèi)聯(lián)維護函數(shù)的兩個版本罕偎,內(nèi)聯(lián)版本和外聯(lián)版本,可以在某些場合使用內(nèi)聯(lián)版本而在其它場合使用外聯(lián)版本漓帅。為了避免代碼重復(fù)锨亏,在外聯(lián)版本的函數(shù)中調(diào)用內(nèi)聯(lián)版本的函數(shù)痴怨。