性能优化:编译器优化选项 -O2/-O3 究竟有多强大?

之前的“性能优化的一般策略及方法”一文中介绍了多种性能优化的方法。根据以往的项目经验,开启编译器优化选项可能是立竿见影、成本最低、效果最好的方式了。

这么说可能还不够直观,举个真实的例子:我所参与的自动驾驶的项目中,无需修改任何代码,仅仅增加一个 -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]

热门相关:陪睡美人   坚如磐石   逍遥小书生   娘娘每天都在洗白   全能千金燃翻天