效能最佳化:編譯器最佳化選項 -O2/-O3 究竟有多強大?

Zijian/TENG發表於2024-03-15

之前的“效能最佳化的一般策略及方法”一文中介紹了多種效能最佳化的方法。根據以往的專案經驗,開啟編譯器最佳化選項可能是立竿見影、成本最低、效果最好的方式了。

這麼說可能還不夠直觀,舉個真實的例子:我所參與的自動駕駛的專案中,無需修改任何程式碼,僅僅增加一個 -O2 選項,程序整體的 CPU loading 可以從 50% 降到 30% 左右,某些關鍵函式的執行時間可以從 1700us 降低到 700us 左右。

編譯器能最佳化能力遠比你想象中的強大!往後翻翻附錄,看看那些多到讓人眼花的最佳化選項你就知道,很多的人工最佳化都是不必要的,編譯器會做得更快,更好,更安全!人工最佳化,不僅會降低程式碼的可讀性和可維護性,而且非常容易引入 bug!

實際上,不管是 -O2 還是 -O3,都是一組最佳化選項的集合,要知道具體做了什麼,可以透過 gcc/g++ 的 -c -Q --help=optimizers 引數

例如我用的 aarch64-unknown-nto-qnx7.1.0-g++ 編譯器,如果想知道加了 -O2 之後開啟了哪些最佳化項,可以透過以下 3 條命令:

$ aarch64-unknown-nto-qnx7.1.0-g++ -c -Q -O2 --help=optimizers > /tmp/O2-opts
$ aarch64-unknown-nto-qnx7.1.0-g++ -c -Q --help=optimizers > /tmp/O-opts
$ diff /tmp/O2-opts /tmp/O-opts | grep enabled
<   -fdevirtualize                              [enabled]
<   -finline-functions-called-once              [enabled]
<   -finline-small-functions                    [enabled]
<   -foptimize-strlen                           [enabled]
<   -freorder-blocks                            [enabled]
<   -freorder-functions                         [enabled]
<   -ftree-switch-conversion                    [enabled]
<   -ftree-tail-merge                           [enabled]
...

隨便看了幾個,就足以感受到編譯器最佳化選項的強大:

  • finline-xxx:行內函數,以避免函式呼叫開銷。順便提一句:程式碼中的 inline 關鍵字只是一個對編譯器的提示,編譯器會根據具體情況作出最佳的選擇,無論是否有 inline 關鍵字
  • fdevirtualize:嘗試把虛擬函式呼叫轉換為直接呼叫,以避免虛擬函式導致的額外開銷
  • freorder-blocks:對函式中的程式碼塊重新排序,以減少分支數、提高程式碼區域性性
  • freorder-functions:對物件中函式重新排序,以提升程式碼區域性性:把經常執行的函式放到 ".text.hot" 節,不常執行的函式放到 ".text.unlikely" 節
    ...

完整的最佳化項很多,具體每個選項的確切解釋需要檢視編譯器手冊。

小結

  1. 如果效能不理想,先檢查是否開啟了編譯器最佳化選項。這可能是最快、最有效的手段了。
  2. 編譯器能最佳化能力遠比你想象中的強大!
  3. 不要在沒有開啟最佳化選項的時候就開始盲目改程式碼,很多都是徒勞,甚至降低效能、引入 bug:編譯器最佳化會做得更快、更安全
  4. 如果開了最佳化選項,你的程式出現問題,不要懷疑編譯器,大機率是因為你的程式碼不規範,使用了 C/C++ “未定義”行為導致的
  5. 需要注意,在汽車領域中,對最佳化選項有一定的限制,比如我的專案中,編譯器的 Safety Manual 明確說明了最大隻支援 -O2 的最佳化等級

附錄

授人以漁

關於這個問題,我第一開始想到的是問 ChatGPT,但是得到的結果並不滿意。然後想到的是 RTFM!

man gcc

線上版本:https://manpages.org/gcc

搜尋關鍵字 /optimiz,很快就找到了我要的答案:

gcc 支援的最佳化選項

 Optimization Options
           -faggressive-loop-optimizations -falign-functions[=n[:m:[n2[:m2]]]] -falign-jumps[=n[:m:[n2[:m2]]]] -falign-labels[=n[:m:[n2[:m2]]]]
           -falign-loops[=n[:m:[n2[:m2]]]] -fno-allocation-dce -fallow-store-data-races -fassociative-math  -fauto-profile  -fauto-profile[=path]
           -fauto-inc-dec  -fbranch-probabilities -fcaller-saves -fcombine-stack-adjustments  -fconserve-stack -fcompare-elim  -fcprop-registers
           -fcrossjumping -fcse-follow-jumps  -fcse-skip-blocks  -fcx-fortran-rules -fcx-limited-range -fdata-sections  -fdce  -fdelayed-branch
           -fdelete-null-pointer-checks  -fdevirtualize  -fdevirtualize-speculatively -fdevirtualize-at-ltrans  -fdse -fearly-inlining  -fipa-sra
           -fexpensive-optimizations  -ffat-lto-objects -ffast-math  -ffinite-math-only  -ffloat-store  -fexcess-precision=style -ffinite-loops
           -fforward-propagate  -ffp-contract=style  -ffunction-sections -fgcse  -fgcse-after-reload  -fgcse-las  -fgcse-lm  -fgraphite-identity
           -fgcse-sm  -fhoist-adjacent-loads  -fif-conversion -fif-conversion2  -findirect-inlining -finline-functions  -finline-functions-called-once
           -finline-limit=n -finline-small-functions -fipa-modref -fipa-cp  -fipa-cp-clone -fipa-bit-cp  -fipa-vrp  -fipa-pta  -fipa-profile
           -fipa-pure-const -fipa-reference  -fipa-reference-addressable -fipa-stack-alignment  -fipa-icf  -fira-algorithm=algorithm
           -flive-patching=level -fira-region=region  -fira-hoist-pressure -fira-loop-pressure  -fno-ira-share-save-slots -fno-ira-share-spill-slots
           -fisolate-erroneous-paths-dereference  -fisolate-erroneous-paths-attribute -fivopts  -fkeep-inline-functions  -fkeep-static-functions
           -fkeep-static-consts  -flimit-function-alignment  -flive-range-shrinkage -floop-block  -floop-interchange  -floop-strip-mine
           -floop-unroll-and-jam  -floop-nest-optimize -floop-parallelize-all  -flra-remat  -flto  -flto-compression-level -flto-partition=alg
           -fmerge-all-constants -fmerge-constants  -fmodulo-sched  -fmodulo-sched-allow-regmoves -fmove-loop-invariants  -fno-branch-count-reg
           -fno-defer-pop  -fno-fp-int-builtin-inexact  -fno-function-cse -fno-guess-branch-probability  -fno-inline  -fno-math-errno  -fno-peephole
           -fno-peephole2  -fno-printf-return-value  -fno-sched-interblock -fno-sched-spec  -fno-signed-zeros -fno-toplevel-reorder  -fno-trapping-math
           -fno-zero-initialized-in-bss -fomit-frame-pointer  -foptimize-sibling-calls -fpartial-inlining  -fpeel-loops  -fpredictive-commoning
           -fprefetch-loop-arrays -fprofile-correction -fprofile-use  -fprofile-use=path -fprofile-partial-training -fprofile-values
           -fprofile-reorder-functions -freciprocal-math  -free  -frename-registers  -freorder-blocks -freorder-blocks-algorithm=algorithm
           -freorder-blocks-and-partition  -freorder-functions -frerun-cse-after-loop  -freschedule-modulo-scheduled-loops -frounding-math
           -fsave-optimization-record -fsched2-use-superblocks  -fsched-pressure -fsched-spec-load  -fsched-spec-load-dangerous
           -fsched-stalled-insns-dep[=n]  -fsched-stalled-insns[=n] -fsched-group-heuristic  -fsched-critical-path-heuristic -fsched-spec-insn-heuristic
           -fsched-rank-heuristic -fsched-last-insn-heuristic  -fsched-dep-count-heuristic -fschedule-fusion -fschedule-insns  -fschedule-insns2
           -fsection-anchors -fselective-scheduling  -fselective-scheduling2 -fsel-sched-pipelining  -fsel-sched-pipelining-outer-loops
           -fsemantic-interposition  -fshrink-wrap  -fshrink-wrap-separate -fsignaling-nans -fsingle-precision-constant  -fsplit-ivs-in-unroller
           -fsplit-loops -fsplit-paths -fsplit-wide-types  -fsplit-wide-types-early  -fssa-backprop  -fssa-phiopt -fstdarg-opt  -fstore-merging
           -fstrict-aliasing -fthread-jumps  -ftracer  -ftree-bit-ccp -ftree-builtin-call-dce  -ftree-ccp  -ftree-ch -ftree-coalesce-vars
           -ftree-copy-prop  -ftree-dce  -ftree-dominator-opts -ftree-dse  -ftree-forwprop  -ftree-fre  -fcode-hoisting -ftree-loop-if-convert
           -ftree-loop-im -ftree-phiprop  -ftree-loop-distribution  -ftree-loop-distribute-patterns -ftree-loop-ivcanon  -ftree-loop-linear
           -ftree-loop-optimize -ftree-loop-vectorize -ftree-parallelize-loops=n  -ftree-pre  -ftree-partial-pre  -ftree-pta -ftree-reassoc
           -ftree-scev-cprop  -ftree-sink  -ftree-slsr  -ftree-sra -ftree-switch-conversion  -ftree-tail-merge -ftree-ter  -ftree-vectorize  -ftree-vrp
           -funconstrained-commons -funit-at-a-time  -funroll-all-loops  -funroll-loops -funsafe-math-optimizations  -funswitch-loops -fipa-ra
           -fvariable-expansion-in-unroller  -fvect-cost-model  -fvpt -fweb  -fwhole-program  -fwpa  -fuse-linker-plugin -fzero-call-used-regs --param
           name=value -O  -O0  -O1  -O2  -O3  -Os  -Ofast  -Og

aarch64-unknown-nto-qnx7.1.0-g++ 加 -O2 相較於預設不加 -O2 增加的最佳化選項(完整列表)

$ aarch64-unknown-nto-qnx7.1.0-g++ -c -Q -O2 --help=optimizers > /tmp/O2-opts
$ aarch64-unknown-nto-qnx7.1.0-g++ -c -Q --help=optimizers > /tmp/O-opts
$ diff /tmp/O2-opts /tmp/O-opts | grep enabled
<   -falign-labels                              [enabled]
<   -fbranch-count-reg                          [enabled]
<   -fcaller-saves                              [enabled]
<   -fcode-hoisting                             [enabled]
<   -fcombine-stack-adjustments                 [enabled]
<   -fcompare-elim                              [enabled]
<   -fcprop-registers                           [enabled]
<   -fcrossjumping                              [enabled]
<   -fcse-follow-jumps                          [enabled]
<   -fdefer-pop                                 [enabled]
<   -fdevirtualize                              [enabled]
<   -fdevirtualize-speculatively                [enabled]
<   -fexpensive-optimizations                   [enabled]
<   -fforward-propagate                         [enabled]
<   -fgcse                                      [enabled]
<   -fguess-branch-probability                  [enabled]
<   -fhoist-adjacent-loads                      [enabled]
<   -fif-conversion                             [enabled]
<   -fif-conversion2                            [enabled]
<   -findirect-inlining                         [enabled]
<   -finline-functions-called-once              [enabled]
<   -finline-small-functions                    [enabled]
<   -fipa-bit-cp                                [enabled]
<   -fipa-cp                                    [enabled]
<   -fipa-icf                                   [enabled]
<   -fipa-icf-functions                         [enabled]
<   -fipa-icf-variables                         [enabled]
<   -fipa-profile                               [enabled]
<   -fipa-pure-const                            [enabled]
<   -fipa-ra                                    [enabled]
<   -fipa-reference                             [enabled]
<   -fipa-sra                                   [enabled]
<   -fipa-vrp                                   [enabled]
<   -fisolate-erroneous-paths-dereference       [enabled]
<   -flra-remat                                 [enabled]
<   -fmove-loop-invariants                      [enabled]
<   -foptimize-sibling-calls                    [enabled]
<   -foptimize-strlen                           [enabled]
<   -fpartial-inlining                          [enabled]
<   -fpeephole2                                 [enabled]
<   -freorder-blocks                            [enabled]
<   -freorder-functions                         [enabled]
<   -frerun-cse-after-loop                      [enabled]
<   -fsched-pressure                            [enabled]
<   -fschedule-insns                            [enabled]
<   -fschedule-insns2                           [enabled]
<   -fsection-anchors                           [enabled]
<   -fshrink-wrap                               [enabled]
<   -fsplit-wide-types                          [enabled]
<   -fssa-phiopt                                [enabled]
<   -fstore-merging                             [enabled]
<   -fstrict-aliasing                           [enabled]
<   -fthread-jumps                              [enabled]
<   -ftree-bit-ccp                              [enabled]
<   -ftree-builtin-call-dce                     [enabled]
<   -ftree-ccp                                  [enabled]
<   -ftree-ch                                   [enabled]
<   -ftree-coalesce-vars                        [enabled]
<   -ftree-copy-prop                            [enabled]
<   -ftree-dce                                  [enabled]
<   -ftree-dominator-opts                       [enabled]
<   -ftree-dse                                  [enabled]
<   -ftree-fre                                  [enabled]
<   -ftree-pre                                  [enabled]
<   -ftree-pta                                  [enabled]
<   -ftree-sink                                 [enabled]
<   -ftree-slsr                                 [enabled]
<   -ftree-sra                                  [enabled]
<   -ftree-switch-conversion                    [enabled]
<   -ftree-tail-merge                           [enabled]
<   -ftree-ter                                  [enabled]
<   -ftree-vrp                                  [enabled]

相關文章