編譯器與程式碼產生

效能剖析導向最佳化(profile-guided optimization,PGO)

/ PGO /

最佳化器不斷對你的程式做猜測:一個 if 通常走哪個分支、哪些函式熱到值得內聯、哪些迴圈跑得頻繁。不執行程式,它只能靠啟發式猜。效能剖析導向最佳化用量測取代那些猜測:你先執行程式、收集它實際行為的資料,再用那份資料重新編譯,以做出更有根據的決定。

PGO 是三步驟的工作流程。第一,建置一個插樁版本,會記錄執行期事實:每個分支各走哪邊的頻率、每個函式被呼叫幾次、哪些路徑是熱的。第二,用具代表性、貼近真實的輸入執行那個版本——一個近似正式使用的工作負載——產生一份剖析檔(一個計數的資料檔)。第三,從同一份原始碼重新編譯,這次把剖析檔餵給編譯器。有了真實頻率,最佳化器做出更精準的選擇:內聯真正熱的函式、把常走的分支佈局成直落(fall-through,使預測路徑更便宜)、把熱程式碼聚在一起以改善指令快取行為、並避免膨脹冷路徑。一個相關的取樣變體是用低開銷的剖析器從正式環境收集剖析,而非用特別的插樁建置。

它重要在於:最佳化器的靜態啟發式常對「哪段程式碼是熱的」判斷錯誤,而 PGO 正是藉由用真相取代猜測,帶來有意義的加速(大型應用上常見可觀的百分比)——這就是回饋導向編譯。誠實的提醒:PGO 的好壞取決於剖析的工作負載。如果你的訓練輸入不像真實使用,你就是為錯誤的情況最佳化、甚至可能拖慢真實工作負載。它讓建置變複雜(編譯、執行、再編譯),而剖析檔會隨程式碼改變而過時。而且它是為效能、不是為正確性的最佳化——它從不改變程式算什麼,只改變編譯器如何分配它的努力優先順序。

$ gcc -O2 -fprofile-generate prog.c -o prog # 1. 建置插樁的二進位檔 $ ./prog < representative_input # 2. 用真實資料執行 -> 寫出剖析檔 $ gcc -O2 -fprofile-use prog.c -o prog # 3. 在量得的剖析檔導向下重新編譯

從真實的執行產生剖析檔,再用它重新編譯,使最佳化器優先處理真正熱的程式碼。

PGO 的好壞取決於訓練工作負載:來自不具代表性輸入的剖析檔會為錯誤的情況最佳化、可能拖慢真實使用;它改變效能、絕不改變正確性,而剖析檔會隨程式碼演進而過時。

又称
PGOfeedback-directed optimizationFDOprofile-driven optimization回饋導向最佳化