編譯器與程式碼產生

最佳化等級(optimization level)

當你要編譯器建置程式時,你也能說它該多努力把結果做快(或做小)。那個旋鈕就是最佳化等級,用像 -O0 或 -O2 的命令列旗標設定。把它調高,就是告訴編譯器跑更多最佳化 pass、跑得更積極;把它調低,編譯就快、輸出也更貼近你寫的東西。

常見等級大致是:-O0 不做最佳化——編譯快、好除錯,產生的程式碼幾乎逐行對應你的原始碼。-O1 做輕量、便宜的最佳化。-O2 是常見的正式預設:一組廣泛的最佳化(內聯、常數傳播、死碼消除、迴圈轉換),搭配合理的編譯時間預算。-O3 比 -O2 更積極,更倚重內聯與向量化,有時產生更大甚至更慢的程式碼。-Os 為小程式碼大小最佳化,-Oz 為盡可能最小的大小最佳化,犧牲速度。每個等級其實是一份預設的 pass 管線,加上各 pass 的門檻(例如一個函式多大仍能被內聯)。

它重要在於:最佳化等級同時改變效能與你除錯時觀察到的東西:在 -O0,變數待在你預期的地方,而在 -O2,它們可能被摺疊掉、只留在暫存器裡、或被重排,使單步執行令人困惑。它也是未定義行為意外出現的地方——隨著你提高等級,UB 驅動的最佳化會被啟用。值得直說的提醒:-O3 不會自動比 -O2 快(有時更慢);各等級在不同編譯器間並未標準化(GCC 的 -O2 與 Clang 的 -O2 啟用不同的 pass);而提高等級永遠不會修好一個 bug——它只能暴露一個一直存在的 bug。

$ gcc -O0 main.c # 不最佳化,好除錯 $ gcc -O2 main.c # 常見的正式等級 $ gcc -Os main.c # 為小程式碼大小最佳化 $ gcc -O3 main.c # 最積極;不一定比 -O2 快

同一份原始碼、四種旋鈕。旗標選擇一份預設的 pass 管線及其門檻。

提高最佳化等級無法修好 bug——它只能揭露一個在 -O0 潛伏的 bug(尤其是未定義行為);而 -O3 不可靠地比 -O2 快,所以要量測而非假設。

又稱
-O flag-O0 through -O3-Os-Ozopt level最佳化選項