[英]C++ How to determine if function has capability for being inlined and actually was?
我對C ++中的inline
函數有疑問。 我知道類似的問題多次出現在此。 我希望我的有點不同。
我知道當你指定一些inline
函數時,它只是編譯器的“建議”。 以防萬一:
inline int func1()
{
return 2;
}
有些代碼稍后
cout << func1() << endl; // replaced by cout << 2 << endl;
所以那里沒有神秘感,但是這樣的情況呢:
inline int func1()
{
return 2;
}
inline int func2()
{
return func1() * 2;
}
inline int func3()
{
return func2() * func1() * 2;
}
等等...
哪些函數有機會內聯,是否有益以及如何檢查編譯器實際執行的操作?
哪些功能有機會內聯
如果執行內聯的工具(1)可以訪問函數的定義(= body),那么任何和所有函數都有機會被內聯...
是有益的嗎?
......並認為這樣做是有益的。 如今,優化器的工作是確定內聯在哪里是有意義的,對於99.9%的程序,程序員可以做的最好的事情就是遠離優化器的方式。 剩下的幾個案例是像Facebook這樣的程序,其中0.3%的性能損失是一個巨大的倒退。 在這種情況下,手動調整優化(以及分析, 分析和分析 )是可行的方法。
如何檢查編譯器實際執行的操作
通過檢查生成的組件。 每個編譯器都有一個標志,使其以“人類可讀”的格式輸出匯編,而不是(或除了)二進制形式的目標文件。
(1)通常,此工具是編譯器,並且內聯在編譯步驟中發生(將源代碼轉換為匯編/目標文件)。 這也是為什么你可能需要使用inline
關鍵字來實際允許編譯器內聯的唯一原因:因為函數的定義必須在正在編譯的轉換單元(=源文件)中可見,並且通常這意味着放置函數定義到頭文件中。 如果沒有inline
,如果頭文件包含在多個翻譯單元中,則會導致多重定義錯誤。
請注意,編譯不是可以進行內聯的唯一階段。 啟用整體程序優化(也稱為鏈接時代碼生成)時,一旦創建了所有目標文件,就會在鏈接時再發生一次優化。 此時, inline
關鍵字完全無關緊要,因為鏈接可以訪問所有函數定義(否則二進制文件不會成功鏈接)。 因此,這是從內聯中獲得最大收益的方法,而不必在編寫代碼時考慮它。 缺點是時間:WPO需要時間來運行,對於大型項目,可能會將鏈接時間延長到不可接受的水平(我個人經歷了一個有點病態的情況,即啟用WPO將程序的鏈接時間從7分鍾縮短到46分鍾)。
想象inline
因為只有一個提示編譯器,有點像register
是舊版本的C ++和C標准。 注意事項, register
已被廢棄(在C ++ 17中)。
哪些功能有機會內聯,是否有益
相信您的編譯器可以做出明智的內聯決策。 要啟用某些特定的調用,編譯器需要知道被調用函數的主體。 您不應該關心編譯器是否在線(理論上)。
在實踐中,使用GCC編譯器:
內聯的決定取決於許多 優化選項 。 使用-O3
可能比使用-O1
更可能發生; 有許多guru選項(如-finline-limit=
和其他)來調整它。
注意個別電話是否內聯。 很可能在第123行的某些調用出現如foo(x)
被內聯,但是在第456行等其他地方的另一個調用事件(對於同一函數foo
)如foo(y)
沒有內聯。
在調試時 ,您可能希望禁用內聯(因為這使調試更方便)。 這可以通過-fno-inline
GCC優化標志(我經常與-g
使用,它要求調試信息)。
always_inline
函數屬性 “強制”內聯,而noinline
阻止它。
如果您使用鏈接時間優化 (LTO)(例如-flto -O2
(或-flto -O3
))進行編譯和鏈接 ,例如在Makefile
使用CXX=g++ -flto -O2
,則可能會在多個翻譯單元之間進行內聯(例如C ++源代碼)文件)。 但是,LTO至少會使編譯時間翻倍(通常情況下更糟)並在編譯期間消耗內存(因此更好地擁有大量RAM),並且通常只會提高幾個百分點的性能(這個經驗法則有奇怪的例外) 。
您可以以不同方式優化函數,例如使用 #pragma GCC optimize ("-O3")
或使用函數屬性 optimize
另請-fprofile-generate
配置文件引導的優化,其中包括-fprofile-generate
等儀器選項以及使用-fprofile-use
和其他優化標志的后一種優化。
如果你g++ -O2 -S -fverbose-asm
聯調用(有時候有些調用)很好奇,請查看生成的匯編程序(例如使用g++ -O2 -S -fverbose-asm
並查看.s
匯編程序文件),或者使用一些內部g++ -O2 -S -fverbose-asm
轉儲選項 。
代碼的可觀察行為(性能除外)不應該依賴於編譯器的內聯決策。 換句話說,不要指望內聯發生(或不發生)。 如果您的代碼在有或沒有優化的情況下表現不同,則可能是錯誤的。 閱讀有關未定義行為的內容 。
另請參閱MILEPOST GCC項目(使用機器學習技術進行優化)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.