gcc-O0-O1-O2-O3四级优化选项及每级分别做什么优化
Gcc 编译优化简介gcc 提供了为了满⾜⽤户不同程度的的优化需要,提供了近百种优化选项,⽤来对{编译时间,⽬标⽂件长度,执⾏效率}这个三维模型进⾏不同的取舍和平衡。优化的⽅法不⼀⽽⾜,总体上将有以下⼏类:1)精简操作指令;2)尽量满⾜cpu的流⽔操作;3)通过对程序⾏为地猜测,重新调整代码的执⾏顺序;4)充分使⽤寄存器;5)对简单的调⽤进⾏展开等等。想全部了解这些编译选项,并在其中挑选适合的选项进⾏优化,⽆疑像个噩梦般的过程。单从gnu的官⽅⽹站上得到的⼿册来看,描述依然⽐较苍⽩,不⾜以完全了解选项的使⽤范围和原理。
(GCC has well over a hundred individual optimization flags and it would be insane to try and describe them all)
幸⽽gcc提供了从O0-O3以及Os这⼏种不同的优化级别供⼤家选择,在这些选项中,包含了⼤部分有效的编译优化选项,并且可以在这个基础上,对某些选项进⾏屏蔽或添加,从⽽⼤⼤降低了使⽤的难度,毕竟,在⼀定基础上进⾏取舍,⽐万事从头开始要好得多。下⾯着重围绕这⼏个不同的级别进⾏简单介绍。(由于gcc不同版本⼿册差异⽐较⼤,以下主要以gcc-3.4.6为参考)
-O0:不做任何优化,这是默认的编译选项。
-
O和-O1:对程序做部分编译优化,对于⼤函数,优化编译占⽤稍微多的时间和相当⼤的内存。使⽤本项优化,编译器会尝试减⼩⽣成代码的尺⼨,以及缩短执⾏时间,但并不执⾏需要占⽤⼤量编译时间的优化。
打开的优化选项:
l -fdefer-pop:延迟栈的弹出时间。当完成⼀个函数调⽤,参数并不马上从栈中弹出,⽽是在多个函数被调⽤后,⼀次性弹出。
l -fmerge-constants:尝试横跨编译单元合并同样的常量(string constants and floating point constants)
l -fthread-jumps:如果某个跳转分⽀的⽬的地存在另⼀个条件⽐较,⽽且该条件⽐较包含在前⼀个⽐较语句之内,那么执⾏本项优化.根据条件是true或者false,前⾯那条分⽀重定向到第⼆条分⽀的⽬的地或者紧跟在第⼆条分⽀后⾯.
l -floop-optimize:执⾏循环优化,将常量表达式从循环中移除,简化判断循环的条件,并且optionally do strength-reduction,或者将循环打开等。在⼤型复杂的循环中,这种优化⽐较显著。
l -fif-conversion:尝试将条件跳转转换为等价的⽆分⽀型式。优化实现⽅式包括条件移动,min,max,设置标志,以及abs指令,以及⼀些算术技巧等。
l -fif-conversion2基本意义相同,没有到更多的解释。
l -fdelayed-branch:这种技术试图根据指令周期时间重新安排指令。它还试图把尽可能多的指令移动到条件分⽀前, 以便最充分的利⽤处理器的治理缓存。
l -fguess-branch-probability:当没有可⽤的profiling feedback或__builtin_expect时,编译器采⽤随机模式猜测分⽀被执⾏的可能性,并移动对应汇编代码的位置,这有可能导致不同的编译器会编译出迥然不同的⽬标代码。
l -fcprop-registers:因为在函数中把寄存器分配给变量, 所以编译器执⾏第⼆次检查以便减少调度依赖性(两个段要求使⽤相同的寄存器)并且删除不必要的寄存器复制操作。
-O2:是⽐O1更⾼级的选项,进⾏更多的优化。Gcc将执⾏⼏乎所有的不包含时间和空间折中的优化。当设置O2选项时,编译器并不进⾏循环打开()loop unrolling以及函数内联。与O1⽐较⽽⾔,O2优化增加了编译时间的基础上,提⾼了⽣成代码的执⾏效率。
O2打开所有的O1选项,并打开以下选项:
l -fforce-mem:在做算术操作前,强制将内存数据copy到寄存器中以后再执⾏。这会使所有的内存引⽤潜在的共同表达式,进⽽产出更⾼效的代码,当没有共同的⼦表达式时,指令合并将排出个别的寄存器
载⼊。这种优化对于只涉及单⼀指令的变量, 这样也许不会有很⼤的优化效果. 但是对于再很多指令(必须数学操作)中都涉及到的变量来说, 这会时很显著的优化, 因为和访问内存中的值相⽐ ,处理器访问寄存器中的值要快的多。
l -foptimize-sibling-calls:优化相关的以及末尾递归的调⽤。通常, 递归的函数调⽤可以被展开为⼀系列⼀般的指令,⽽不是使⽤分⽀。这样处理器的指令缓存能够加载展开的指令并且处理他们, 和指令保持为需要分⽀操作的单独函数调⽤相⽐, 这样更快。
l -fstrength-reduce:这种优化技术对循环执⾏优化并且删除迭代变量。迭代变量是捆绑到循环计数器的变量, ⽐如使⽤变量, 然后使⽤循环计数器变量执⾏数学操作的for-next循环。
l -fcse-follow-jumps:在公⽤⼦表达式消元时,当⽬标跳转不会被其他路径可达,则扫描整个的跳转表达式。例如,当公⽤⼦表达式消元时遇到if...语句时,当条为false时,那么公⽤⼦表达式消元会跟随着跳转。
l -fcse-skip-blocks:与-fcse-follow-jumps类似,不同的是,根据特定条件,跟随着cse跳转的会是整个的blocks
l -frerun-cse-after-loop:在循环优化完成后,重新进⾏公⽤⼦表达式消元操作。
l -frerun-loop-opt:两次运⾏循环优化 l -fgcse:执⾏全局公⽤⼦表达式消除pass。这个pass还执⾏全局常量和copy propagation。这些优化操作试图分析⽣成的汇编语⾔代码并且结合通⽤⽚段,消除冗余的代码段。如果代码使⽤计算性的goto, gcc指令推荐使⽤-fno-gcse选项。
l-fgcse-lm:全局公⽤⼦表达式消除将试图移动那些仅仅被⾃⾝存储kill的装载操作的位置。这将允许将循环内的load/store操作序列中
的load转移到循环的外⾯(只需要装载⼀次),⽽在循环内改变成copy/store序列。在选中-fgcse后,默认打开。
l-fgcse-sm:当⼀个存储操作pass在⼀个全局公⽤⼦表达式消除的后⾯,这个pass将试图将store操作转移到循环外⾯去。如果与-fgcse-lm配合使⽤,那么load/store操作将会转变为在循环前load,在循环后store,从⽽提⾼运⾏效率,减少不必要的操作。
l -fgcse-las:全局公⽤⼦表达式消除pass将消除在store后⾯的不必要的load操作,这些load与store通常是同⼀块存储单元(全部或局部)
l-fdelete-null-pointer-checks:通过对全局数据流的分析,识别并排出⽆⽤的对空指针的检查。编译器假设间接引⽤空指针将停⽌程
序。如果在间接引⽤之后检查指针,它就不可能为空。
l-fexpensive-optimizations:进⾏⼀些从编译的⾓度来说代价⾼昂的优化(这种优化据说对于程序执⾏未必有很⼤的好处,甚⾄有可能降低执⾏效率,具体不是很清楚)
l-fregmove:编译器试图重新分配move指令或者其他类似操作数等简单指令的寄存器数⽬,以便最⼤化的捆绑寄存器的数⽬。这种优化尤其对双操作数指令的机器帮助较⼤。
l-fschedule-insns:编译器尝试重新排列指令,⽤以消除由于等待未准备好的数据⽽产⽣的延迟。这种优化将对慢浮点运算的机器以及需要load memory的指令的执⾏有所帮助,因为此时允许其他指令执⾏,直到load memory的指令完成,或浮点运算的指令再次需要cpu。 l
-fschedule-insns2:与-fschedule-insns相似。但是当寄存器分配完成后,会请求⼀个附加的指令计划pass。这种优化对寄存器较⼩,并且load memory操作时间⼤于⼀个时钟周期的机器有⾮常好的效果。
l -fsched-interblock:这种技术使编译器能够跨越指令块调度指令。这可以⾮常灵活地移动指令以便等待期间完成的⼯作最⼤化。
l-fsched-spec-load:允许⼀些load指令进⾏⼀些投机性的动作。(具体不详)相同功能的还有-fsched-spec-load-dangerous,允许更多的load指令进⾏投机性操作。这两个选项在选中-fschedule-insns时默
认打开。
l -fcaller-saves:通过存储和恢复call调⽤周围寄存器的⽅式,使被call调⽤的value可以被分配给寄存器,这种只会在看上去能产⽣更好的代码的时候才被使⽤。(如果调⽤多个函数, 这样能够节省时间, 因为只进⾏⼀次寄存器的保存和恢复操作, ⽽不是在每个函数调⽤中都进⾏。)
重定向过多是什么意思l-fpeephole2:允许计算机进⾏特定的观察孔优化(这个不晓得是什么意思),-fpeephole与-fpeephole2的差别在于不同的编译器采⽤不同的⽅式,由的采⽤-fpeephole,有的采⽤-fpeephole2,也有两种都采⽤的。
l -freorder-blocks:在编译函数的时候重新安排基本的块,⽬的在于减少分⽀的个数,提⾼代码的局部性。
l-freorder-functions:在编译函数的时候重新安排基本的块,⽬的在于减少分⽀的个数,提⾼代码的局部性。这种优化的实施依赖特定的已存在的信息:.text.hot⽤于告知访问频率较⾼的函数,.text.unlikely⽤于告知基本不被执⾏的函数。
l-fstrict-aliasing:这种技术强制实⾏⾼级语⾔的严格变量规则。对于c和c++程序来说, 它确保不在数据类型之间共享变量. 例如, 整数变量不和单精度浮点变量使⽤相同的内存位置。
l-funit-at-a-time:在代码⽣成前,先分析整个的汇编语⾔代码。这将使⼀些额外的优化得以执⾏,但是在编译器间需要消耗⼤量的内存。(有资料介绍说:这使编译器可以重新安排不消耗⼤量时间的代码以便优化指令缓存。)
l -falign-functions:这个选项⽤于使函数对准内存中特定边界的开始位置。⼤多数处理器按照页⾯读取内存,并且确保全部函数代码位于单⼀内存页⾯内, 就不需要叫化代码所需的页⾯。
l-falign-jumps:对齐分⽀代码到2的n次⽅边界。在这种情况下,⽆需执⾏傀儡指令(dummy operations)
l -falign-loops:对齐循环到2的n次幂边界。期望可以对循环执⾏多次,⽤以补偿运⾏dummy operations所花费的时间。
l -falign-labels:对齐分⽀到2的n次幂边界。这种选项容易使代码速度变慢,原因是需要插⼊⼀些dummy operations当分⽀抵
达usual flow of the code.
l -fcrossjumping:这是对跨越跳转的转换代码处理,以便组合分散在程序各处的相同代码。这样可以减少代码的长度,但是也许不会对程序性能有直接影响。
-O3:⽐O2更进⼀步的进⾏优化。
在包含了O2所有的优化的基础上,⼜打开了以下优化选项:
l -finline-functions:内联简单的函数到被调⽤函数中。由编译器启发式的决定哪些函数⾜够简单可以做这种内联优化。默认情况下,编译器限制内联的尺⼨,3.4.6中限制为600(具体含义不详,指令条数或代码size?)可以通过-finline-limit=n改变这个长度。这种优化技术不为函数创建单独的汇编语⾔代码,⽽是把函数代码包含在调度程序的代码中。对于多次被调⽤的函数来说, 为每次函数调⽤复制函数代码。虽然这样对于减少代码长度不利, 但是通过最充分的利⽤指令缓存代码, ⽽不是在每次函数调⽤时进⾏分⽀操作, 可以提⾼性能。
l -fweb:构建⽤于保存变量的伪寄存器⽹络。伪寄存器包含数据, 就像他们是寄存器⼀样, 但是可以使⽤各种其他优化技术进⾏优化, ⽐
如cse和loop优化技术。这种优化会使得调试变得更加的不可能,因为变量不再存放于原本的寄存器中。
l -frename-registers:在寄存器分配后,通过使⽤registers left over来避免预定代码中的虚假依赖。这会使调试变得⾮常困难,因为变量不再存放于原本的寄存器中了。
l -funswitch-loops:将⽆变化的条件分⽀移出循环,取⽽代之的将结果副本放⼊循环中。
-Os:主要是对程序的尺⼨进⾏优化。打开了⼤部分O2优化中不会增加程序⼤⼩的优化选项,并对程序代码的⼤⼩做更深层的优化。(通常我们不需要这种优化)Os会关闭如下选项: -falign-functions -falign-jumps -falign-loops  -falign-labels  -freorder-blocks  -fprefetch-loop-arrays
优化介绍⼩结O0选项不进⾏任何优化,在这种情况下,编译器尽量的缩短编译消耗(时间,空间),此时,debug会产出和程序预期的结果。当程序运⾏被断点打断,此时程序内的各种声明是独⽴的,我们可以任意的给变量赋值,或者在函数体内把程序计数器指到其他语句,以及从源程序中精确地获取你期待的结果.
O1优化会消耗少多的编译时间,它主要对代码的分⽀,常量以及表达式等进⾏优化。
O2会尝试更多的寄存器级的优化以及指令级的优化,它会在编译期间占⽤更多的内存和编译时间。
O3在O2的基础上进⾏更多的优化,例如使⽤伪寄存器⽹络,普通函数的内联,以及针对循环的更多优化。
Os主要是对代码⼤⼩的优化,我们基本不⽤做更多的关⼼。通常各种优化都会打乱程序的结构,让调试⼯作变得⽆从着⼿。并且会打乱执⾏顺序,依赖内存操作顺序的程序需要做相关处理才能确保程序的正确性。
优化代码有可能带来的问题
1.调试问题:正如上⾯所提到的,任何级别的优化都将带来代码结构的改变。例如:对分⽀的合并和消除,对公⽤⼦表达式的消除,对循环内load/store操作的替换和更改等,都将会使⽬标代码的执⾏顺序变得⾯⽬全⾮,导致调试信息严重不⾜。
2.内存操作顺序改变所带来的问题:在O2优化后,编译器会对影响内存操作的执⾏顺序。例如:-fschedule-insns允许数据处理时先完成其他的指令;-fforce-mem有可能导致内存与寄存器之间的数据产⽣类似脏数据的不⼀致等。对于某些依赖内存操作顺序⽽进⾏的逻辑,需要做严格的处理后才能进⾏优化。例如,采⽤volatile关键字限制变量的操作⽅式,或者利⽤barrier迫使cpu严格按照指令序执⾏的。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。