gcc -o 优化选项

优化选项

查查gcc手册就知道了,每个编译选项都控制着不同的优化选项下面从网络上copy过来的,真要用到这些还是推荐查阅手册 
-O设置一共有五种:-O0、-O1、-O2、-O3和-Os。 

除了-O0以外,每一个-O设置都会多启用几个选项,请查阅gcc手册的优化选项章节,以便了解每个-O等级启用了哪些选项及它们有何作用。 

让我们来逐一考察各个优化等级: 

-O0:这个等级(字母“O”后面跟个零)关闭所有优化选项,也是CFLAGS或CXXFLAGS中没有设置-O等级时的默认等级。这样就不会优化代码,这通常不是我们想要的。 
-O1:这是最基本的优化等级。编译器会在不花费太多编译时间的同时试图生成更快更小的代码。这些优化是非常基础的,但一般这些任务肯定能顺利完成。 
-O2:-O1的进阶。这是推荐的优化等级,除非你有特殊的需求。-O2会比-O1启用多一些标记。设置了-O2后,编译器会试图提高代码性能而不会增大体积和大量占用的编译时间。 
-O3:这是最高最危险的优化等级。用这个选项会延长编译代码的时间,并且在使用gcc4.x的系统里不应全局启用。自从3.x版本以来gcc的行为已经有了极大地改变。在3.x,-O3生成的代码也只是比-O2快一点点而已,而gcc4.x中还未必更快。用-O3来编译所有的软件包将产生更大体积更耗内存的二进制文件,大大增加编译失败的机会或不可预知的程序行为(包括错误)。这样做将得不偿失,记住过犹不及。在gcc 4.x.中使用-O3是不推荐的。 
-Os:这个等级用来优化代码尺寸。其中启用了-O2中不会增加磁盘空间占用的代码生成选项。这对于磁盘空间极其紧张或者CPU缓存较小的机器非常有用。但也可能产生些许问题,因此软件树中的大部分ebuild都过滤掉这个等级的优化。使用-Os是不推荐的。

 

 编译的优化选项

这些选项控制多种优化措施:


 

-O1

优化.对于大函数,优化编译占用稍微多的时间和相当大的内存.

不使用`-O'选项时,编译器的目标是减少编译的开销,使编译结果能够调试.语句是独立的:如果在 两条语句之间用断点中止程序,你可以对任何变量重新赋值,或者在函数体内把程序计数器指到其他语句,以及从源程序中 精确地获取你期待的结果.

不使用`-O'选项时,只有声明了register的变量才分配使用寄存器.编译结果比不用 `-O'选项的PCC要略逊一筹.

使用了`-O'选项,编译器会试图减少目标码的大小和执行时间.

如果指定了`-O'选项, `-fthread-jumps'`-fdefer-pop'选项将被 打开.在有delay slot的机器上, `-fdelayed-branch'选项将被打开.在即使没有帧指针 (frame pointer)也支持调试的机器上, `-fomit-frame-pointer'选项将被打开.某些机器上 还可能会打开其他选项.

-O2

多优化一些.除了涉及空间和速度交换的优化选项,执行几乎所有的优化工作.例如不进行循环展开(loop unrolling)和函数内嵌(inlining).-O选项比较,这个选项既增加了编译时间,也提高了生成代码的 运行效果.

-O3

优化的更多.除了打开-O2所做的一切,它还打开了-finline-functions选项.

-O0

不优化.

如果指定了多个-O选项,不管带不带数字,最后一个选项才是生效的选项.

诸如`-fflag'此类的选项描述一些机器无关的开关.大多数开关具有肯定和否定两种格式; `-ffoo'开关选项的否定格式应该是`-fno-foo'.下面的列表只展示了一种格式---那个不是 默认选项的格式.你可以通过去掉或添加`no-'构造出另一种格式.

-ffloat-store

不要在寄存器中存放浮点变量.这样可以防止某些机器上不希望的过高精度,68000的浮点寄存器(来自 68881)保存的精度超过了double应该具有的精度.

对于大多数程序,过高精度只有好处.但是有些程序严格依赖于IEEE浮点数的定义.对这样的程序可以使用 `-ffloat-store'选项.

-fmemoize-lookups

 

-fsave-memoized

使用探索法(heuristic)进行更快的编译(仅对C++).默认情况下不使用探索法.由于探索法只对某些输入文件 有效,其他程序的编译速度会变得更慢.

第一次编译器必须对成员函数(或对成员数据的引用)建立一个调用.它必须(1)判断出这个类是否实现了那个名字的 成员函数; (2)决定调用哪个成员函数(涉及到推测需要做哪种类型转换); (3)检查成员函数对调用者是否可见.所有这些构成 更慢的编译.一般情形,第二次对成员函数(或对成员数据的引用)建立的调用,必须再次经过相同长度的处理.这意味着象 这样的代码

cout << "This " << p << "has " << n << " legs.\n";

对整个三步骤要做六次遍历.通过使用软件缓存, ``命中''能够显著地减少这种代价.然而不幸的是,使用这种缓存 必须实现其他机制,带来了它自己的开销. `-fmemoize-lookups'选项打开软件缓存.

因为函数的正文环境不同,函数对成员和成员函数的访问权(可见性)也可能不同g++可能需要刷新缓存使用`-fmemoize-lookups'选项,每编译完一个函数就刷新缓存.`-fsave-memoized'选项 也启用同样的缓存,但是当编译器发觉最后编译的函数的正文环境产生的访问权和下一个待编译的函数相同,编译器就 保留缓存内容.这对某个类定义许多成员函数时非常有用:除了某些其他类的友函数,每个成员函数拥有和其他成员函数完全一样的访问权,因而无需刷新缓存.

-fno-default-inline

默认为不要把成员函数内嵌,因为它们定义在类的作用域内(C++).

-fno-defer-pop

一旦函数返回,参数就立即弹出.对于那些调用函数后必须弹出参数的机器,编译器一般情况下让几次函数调用的参数 堆积在栈上,然后一次全部弹出.

-fforce-mem

做数学运算前把将要使用的内存操作数送入寄存器.通过把内存访问转换成潜在的公共子表达式,它可能产生较好的目标码如果它们不是公共子表达式,指令组合应该消除各自的寄存器载荷.我乐意倾听不同意见.

-fforce-addr

做数学运算前把将要使用的内存地址常数送入寄存器.它可能和`-fforce-mem'一样产生较好的 目标码.我乐意倾听不同意见.

-fomit-frame-pointer

对于不需要帧指针(frame pointer)的函数,不要在寄存器中保存帧指针.这样能够避免保存,设置和恢复 帧指针的指令;同时对许多函数提供一个额外的寄存器但是在大多数机器上将无法调试.

某些机器上,Vax,这个选项无效,因为标准调用序列自动处理帧指针,通过假装不存在而不保存任何东西.机器描述宏 FRAME_POINTER_REQUIRED控制目标机是否支持这个选项.

-finline-functions

把所有简单的函数集成进调用者.编译器探索式地决定哪些函数足够简单,值得这种集成.

如果集成了所有给定函数的调用,而且函数声明为static,那么一般说来GCC有权不按汇编代码输出函数.

-fcaller-saves

允许在寄存器里分配数值,但是这个方案通常受到各个函数调用的冲击,因此GCC生成额外的代码,在函数调用的 前后保存和复原寄存器内容.仅当生成代码看上去优于反之结果时才实现这样的分配.

某些机器上该选项默认为允许,通常这些机器没有调用保护寄存器代替使用.

-fkeep-inline-functions

即使集成了某个函数的所有调用,而且该函数声明为static,仍然输出这个函数一个独立的,运行时可调用 的版本.

-fno-function-cse

不要把函数地址存入寄存器;让调用固定函数的指令显式给出函数地址.

这个选项产生效率较低的目标码,但是如果不用这个选项,某些不寻常的hack,改变汇编器的输出,可能因优化而带来 困惑.

-fno-peephole

禁止任何机器相关的peephole优化.

-ffast-math

这个选项出于速度优化,允许GCC违反某些ANSIIEEE规则/规格.例如,它允许编译器假设sqrt 函数的参数是非负数.

这个选项不被任何`-O'选项打开,因为对于严格依靠IEEEANSI规则/规格实现的数学函数,程序可能 会产生错误的结果.

下列选项控制特定的优化. `-O2'选项打开下面的大多数优化项,除了`-funroll-loops'和 `-funroll-all-loops'.

`-O'选项通常打开`-fthread-jumps'`-fdelayed-branch优化项,但是特定的机器上的默认优化项有可能改变.

如果特别情况下非常需要``微调''优化,你可以使用下面的选项.

-fstrength-reduce

执行循环强度缩小(loop strength reduction)优化,并且消除重复变量.

-fthread-jumps

执行优化的地点是,如果某个跳转分支的目的地存在另一个条件比较,而且该条件比较包含在前一个比较语句之内,那么 执行优化.根据条件是true或者false,前面那条分支重定向到第二条分支的目的地或者紧跟在第二条分支后面.

-funroll-loops

执行循环展开(loop unrolling)优化.仅对循环次数能够在编译时或运行时确定的循环实行.

-funroll-all-loops

执行循环展开(loop unrolling)优化.对所有循环实行.通常使程序运行的更慢.

-fcse-follow-jumps

在公共子表达式消元(common subexpressionelimination)的时候,如果没有其他路径到达某个跳转的 目的地,就扫过这条jump指令.例如,如果CSE遇到带有else从句的if语句,当条件测试为 false, CSE就跟在jump后面.

-fcse-skip-blocks

它类似于`-fcse-follow-jumps'选项,但是CSE跟在条件跳转后面,条件跳转跳过了 语句块(block).如果CSE遇到一条简单的if语句,不带else从句, `-fcse-skip-blocks'选项将导致CSE跟在if产生的跳转后面.

-frerun-cse-after-loop

执行循环优化后,重新进行公共子表达式消元.

-felide-constructors

如果看上去合理就省略构造子(C++).根据这个选项,对于下面的代码, GNU C++直接从调用foo 初始化y,而无需通过临时变量:

A foo ();A y = foo ();

如果没有这个选项, GNU C++首先通过调用类型合适的构造子初始化y;然后把 foo的结果赋给临时变量;最后,用临时变量替换`y'的初始值.

ANSI C++标准草案规定了默认行为(`-fno-elide-constructors').如果程序的构造子存在 副效应, `-felide-constructors'选项能够使程序有不同的表现,因为可能忽略一些构造子的调用.

-fexpensive-optimizations

执行一些相对开销较大的次要优化.

-fdelayed-branch

如果对目标机支持这个功能,它试图重新排列指令,以便利用延迟分支(delayed branch)指令后面的指令空隙.

-fschedule-insns

如果对目标机支持这个功能,它试图重新排列指令,以便消除因数据未绪造成的执行停顿.这可以帮助浮点运算或内存访问 较慢的机器调取指令,允许其他指令先执行,直到调取指令或浮点运算完成.

-fschedule-insns2

类似于`-fschedule-insns'选项,但是在寄存器分配完成后,需要一个额外的指令调度过程.对于 寄存器数目相对较少,而且取内存指令大于一个周期的机器,这个选项特别有用.

 

 

 

 优化选项及每级分别做什么优化

相关博客http://blog.chinaunix.net/uid-24954950-id-2956476.html

相关博客http://blog.csdn.net/misiter/article/details/7514428

相关博客http://blog.chinaunix.net/uid-23916171-id-2653114.html

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:尝试将条件跳转转换为等价的无分支型式。优化实现方式包括条件移动,minmax,设置标志,以及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...else...语句时,当条为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操作,这些loadstore通常是同一块存储单元(全部或局部) 

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:这种技术强制实行高级语言的严格变量规则。 对于cc++程序来说它确保不在数据类型之间共享变量例如整数变量不和单精度浮点变量使用相同的内存位置。 

l -funit-at-a-time:在代码生成前,先分析整个的汇编语言代码。这将使一些额外的优化得以执行,但是在编译器间需要消耗大量的内存。(有资料介绍说:这使编译器可以重新安排不消耗大量时间的代码以便优化指令缓存。) 

l -falign-functions:这个选项用于使函数对准内存中特定边界的开始位置。 大多数处理器按照页面读取内存,并且确保全部函数代码位于单一内存页面内就不需要叫化代码所需的页面。 

l -falign-jumps:对齐分支代码到2n次方边界。在这种情况下,无需执行傀儡指令(dummy operations 

l -falign-loops:对齐循环到2n次幂边界。期望可以对循环执行多次,用以补偿运行dummy operations所花费的时间。 

l -falign-labels:对齐分支到2n次幂边界。这种选项容易使代码速度变慢,原因是需要插入一些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:构建用于保存变量的伪寄存器网络。 伪寄存器包含数据就像他们是寄存器一样但是可以使用各种其他优化技术进行优化比如cseloop优化技术。这种优化会使得调试变得更加的不可能,因为变量不再存放于原本的寄存器中。 

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会尝试更多的寄存器级的优化以及指令级的优化,它会在编译期间占用更多的内存和编译时间。 

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

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

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

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

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

 


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值