函式內聯(function inlining)
呼叫一個函式有一點小成本:程式跳走、設置引數、執行函式本體、再跳回來。對一個被呼叫上百萬次的小幫手而言,這些雜務可能遠大於實際工作。函式內聯就是消除這個呼叫的最佳化,把被呼叫者的本體直接貼進呼叫者裡,彷彿你在呼叫點手寫展開了它。
具體而言,當編譯器決定把 g() 裡對 f() 的呼叫內聯,它就用 f 本體的一份副本取代呼叫指令,並把實際引數代入 f 的參數。好處不只是省下呼叫與返回的開銷:一旦 f 的程式碼坐進 g 裡,最佳化器就能跨越舊邊界看見全貌。你傳入的常數現在能被摺疊、分支能被解決、死路能被刪除,進一步的最佳化接連發生。這正是為什麼內聯常被稱為「促成型最佳化」——它最大的好處是為其他 pass 解鎖的工作。
它重要在於:內聯是 -O2 下影響最大的最佳化之一,也是小型存取函式在執行期幾乎零成本的原因。但它受一個成本模型(cost model)支配,那是一個啟發式,權衡預期加速與程式碼膨脹:把一個大函式內聯到許多呼叫點會讓二進位檔變大、可能傷害指令快取行為、甚至拖慢速度。所以編譯器對小或熱的被呼叫者積極內聯、對大的婉拒。誠實的提醒:C 與 C++ 的 inline 關鍵字是一個提示而非命令——由編譯器決定;而更多內聯不一定更好,這又是一個該量測而非假設的地方。
static int sq(int x) { return x * x; } int f(void) { return sq(5); } // 把 sq 內聯進 f、再做常數摺疊之後: int f(void) { return 25; } // 呼叫消失了,5*5 也被預先算出
內聯移除了呼叫,接著把 5 暴露為常數,使本體化簡為 25——這就是促成效果。
inline 關鍵字是提示,不是保證:編譯器的成本模型有最終決定權,而過度內聯會讓二進位檔變大、拖累指令快取,所以它不是「一定更快」。