GCC命令详解与实践(五) -- 代码优化选项

代码优化选项,顾名思义就是用来优化代码的编译选项。

在没有任何优化的情况下,编译器的目标是降低编译成本,并输出预期的结果。

打开优化选项后,编译器会以牺牲编译时间和调试程序的能力为代价来改进性能和/或代码大小。

代码优化的选项有很多种,但我们通常通过设置代码优化等级来告诉编译器该如何优化代码。主要分为以下四个等级。

gcc -O0

用法:gcc -O0

作用:默认配置,不优化。

例如:

gcc -O0 foo.c

gcc -O1 或 gcc -O

用法:gcc -O1

作用:优化,对于-O1/-O编译选项,编译器试图优化代码大小和执行时间,但不执行任何会占用大量
编译时间的优化。

例如:

gcc -O1 foo.c
或
gcc -O foo.c

以下是-O使能的所有编译优化选项。

-fauto-inc-dec 
-fbranch-count-reg 
-fcombine-stack-adjustments 
-fcompare-elim 
-fcprop-registers 
-fdce 
-fdefer-pop 
-fdelayed-branch 
-fdse 
-fforward-propagate 
-fguess-branch-probability 
-fif-conversion 
-fif-conversion2 
-finline-functions-called-once 
-fipa-profile 
-fipa-pure-const 
-fipa-reference 
-fipa-reference-addressable 
-fmerge-constants 
-fmove-loop-invariants 
-fomit-frame-pointer 
-freorder-blocks 
-fshrink-wrap 
-fshrink-wrap-separate 
-fsplit-wide-types 
-fssa-backprop 
-fssa-phiopt 
-ftree-bit-ccp 
-ftree-ccp 
-ftree-ch 
-ftree-coalesce-vars 
-ftree-copy-prop 
-ftree-dce 
-ftree-dominator-opts 
-ftree-dse 
-ftree-forwprop 
-ftree-fre 
-ftree-phiprop 
-ftree-pta 
-ftree-scev-cprop 
-ftree-sink 
-ftree-slsr 
-ftree-sra 
-ftree-ter 
-funit-at-a-time

gcc -O2

用法:gcc -O2

作用:更加优化,对于-O2编译选项,GCC执行几乎所有不包含时间和空间权衡的优化。与-O相比,此选项增加了编译时间,但提高了代码的效率。

例如:

gcc -O2 foo.c

-O2除了打开-O指定的所有优化之外,它还打开了以下优化:

-falign-functions  -falign-jumps 
-falign-labels  -falign-loops 
-fcaller-saves 
-fcode-hoisting 
-fcrossjumping 
-fcse-follow-jumps  -fcse-skip-blocks 
-fdelete-null-pointer-checks 
-fdevirtualize  -fdevirtualize-speculatively 
-fexpensive-optimizations 
-ffinite-loops 
-fgcse  -fgcse-lm  
-fhoist-adjacent-loads 
-finline-functions 
-finline-small-functions 
-findirect-inlining 
-fipa-bit-cp  -fipa-cp  -fipa-icf 
-fipa-ra  -fipa-sra  -fipa-vrp 
-fisolate-erroneous-paths-dereference 
-flra-remat 
-foptimize-sibling-calls 
-foptimize-strlen 
-fpartial-inlining 
-fpeephole2 
-freorder-blocks-algorithm=stc 
-freorder-blocks-and-partition  -freorder-functions 
-frerun-cse-after-loop  
-fschedule-insns  -fschedule-insns2 
-fsched-interblock  -fsched-spec 
-fstore-merging 
-fstrict-aliasing 
-fthread-jumps 
-ftree-builtin-call-dce 
-ftree-pre 
-ftree-switch-conversion  -ftree-tail-merge 
-ftree-vrp

gcc -O3

用法:gcc -O3

作用:比-O2更优化,对于-O3编译选项,在-O2的基础上,打开了更多的优化项。

例如:

gcc -O3 foo.c

-O3除了打开所有-O2指定的优化之外,还打开以下优化:

-fgcse-after-reload 
-fipa-cp-clone
-floop-interchange 
-floop-unroll-and-jam 
-fpeel-loops 
-fpredictive-commoning 
-fsplit-loops 
-fsplit-paths 
-ftree-loop-distribution 
-ftree-loop-vectorize 
-ftree-partial-pre 
-ftree-slp-vectorize 
-funswitch-loops 
-fvect-cost-model 
-fvect-cost-model=dynamic 
-fversion-loops-for-strides

除了以上几个优化选项之外,还有针对软件大小的优化。

gcc -Os

用法:gcc -Os

作用:优化代码大小。除了那些经常会增加代码大小选项,-Os支持所有-O2优化。

例如:

gcc -Os foo.c

优化介绍小结:

O0选项不进行任何优化,在这种情况下,编译器尽量的缩短编译消耗(时间,空间),产出的结果也是可以预知的。

O1优化会消耗少多的编译时间,它主要对代码的分支,常量以及表达式等进行优化。

O2会尝试更多的寄存器级的优化以及指令级的优化,它会在编译期间占用更多的内存和编译时间。

O3在O2的基础上进行更多的优化,例如使用伪寄存器网络,普通函数的内联,以及针对循环的更多优化。

Os主要是对代码大小的优化,我们基本不用做更多的关心。 通常各种优化都会打乱程序的结构,让调试工作变得无从着手。并且会打乱执行顺序,依赖内存操作顺序的程序需要做相关处理才能确保程序的正确性。

优化代码有可能带来的问题:

1.调试问题:正如上面所提到的,任何级别的优化都将带来代码结构的改变。例如:对分支的合并和消除,对公用子表达式的消除,对循环内load/store操作的替换和更改等,都将会使目标代码的执行顺序变得面目全非,导致调试信息严重不足。

2.内存操作顺序改变所带来的问题:在O2优化后,编译器会对影响内存操作的执行顺序。例如:-fschedule-insns允许数据处理时先完成其他的指令;-fforce-mem有可能导致内存与寄存器之间的数据产生类似脏数据的不一致等。对于某些依赖内存操作顺序而进行的逻辑,需要做严格的处理后才能进行优化。例如,采用volatile关键字限制变量的操作方式,或者利用barrier迫使cpu严格按照指令序执行的。