gcc的三级优化到底优化了哪些

本文深入探讨了GNU编译器集合(GCC)提供的多种优化技术,包括基础级别的代码调整、高级别的算法选择及构造,以及通过构建表驱动状态机实现的极致优化。文中详细介绍了不同级别的优化选项,如-O1至-O3,以及各种具体的优化技术,如-fmerge-constants、-fforce-mem等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GNU编译器提供-O选项供程序优化使用:
-O 提供基础级别的优化
-O2 提供更加高级的代码优化,会占用更长的编译时间
-O3 提供最高级的代码优化
-O4 不优化,这是默认值
不同的优化级别使用的优化技术也可以单独的应用于代码。 可以使用-f命令行选项引用每个
单独的优化技术。

第一级:代码调整 
代码调整是一种局部的思维方式;基本上不触及算法层级;它面向的是代码,而不是问题; 所以:语句调整,用汇编重写、指令调整、换一种语言实现、换一个编译器、循环展开、参数传递优化等都属于这一级; 
这个级别的优化需要掌握大量的小的优化技巧和知识,需要不断的积累; 
简单的语句调整、公共表达式提取、废代码删除等当前的很多编译器也能做到了,但也需要了解一些编译器的优化能力使自己的代码配合编译器做好优化; 
用汇编重写并不是简单把高级语言改写为汇编实现,那样写的汇编很可能没有当今的编译器产生的代码好,所以如果决定用汇编实现,那就应该按照汇编的角度来规划自己的实现,适当的参考编译器生成的汇编码也是可取的(特别是新手,我也一样);在某些领域,使用CPU的新特性和新的指令集等将产生巨大的性能收益,这些地方经常采用汇编来实现。
在优化的第一个级别执行基础代码的优化。 这个级别试图执行9种单独的优化功能: 
-fdefer-pop: 这种优化技术与汇编语言代码在函数完成时如何进行操作有关。 一般 
情况下, 函数的输入值被保存在堆栈中并且被函数访问。 函数返回时, 输入值还在 
堆栈中。 一般情况下, 函数返回之后, 输入值被立即弹出堆栈。这样做会使堆栈中 
的内容有些杂乱。
-fmerge-constans: 使用这种优化技术, 编译器试图合并相同的常量. 这一特性有 
时候会导致很长的编译时间, 因为编译器必须分析c或者c++程序中用到的每个常量, 
并且相互比较他们.
-fthread-jumps: 使用这种优化技术与编译器如果处理汇编代码中的条件和非条件 
分支有关。 在某些情况下, 一条跳转指令可能转移到另一条分支语句。 通过一连串 
跳转, 编译器确定多个跳转之间的最终目标并且把第一个跳转重新定向到最终目标。
-floop-optimize: 通过优化如何生成汇编语言中的循环, 编译器可以在很大程序上 
提高应用程序的性能。 通常, 程序由很多大型且复杂的循环构成。 通过删除在循环 
内没有改变值的变量赋值操作, 可以减少循环内执行指令的数量, 在很大程度上提高 
性能。 此外优化那些确定何时离开循环的条件分支, 以便减少分支的影响。
-fif-conversion: if-then语句应该是应用程序中仅次于循环的最消耗时间的部分。 
简单的if-then语句可能在最终的汇编语言代码中产生众多的条件分支。 通过减少 
或者删除条件分支, 以及使用条件传送 设置标志和使用运算技巧来替换他们, 编译 
器可以减少if-then语句中花费的时间量。
-fif-conversion2: 这种技术结合更加高级的数学特性, 减少实现if-then语句所
需的条件分支。
-fdelayed-branch: 这种技术试图根据指令周期时间重新安排指令。 它还试图把 
尽可能多的指令移动到条件分支前, 以便最充分的利用处理器的治理缓存。
-fguess-branch-probability: 就像其名称所暗示的, 这种技术试图确定条件分支最可 
能的结果, 并且相应的移动指令, 这和延迟分支技术类似。 因为在编译时预测代码的安排, 
所以使用这一选项两次编译相同的c或者c++代码很可能会产生不同的汇编语言代码, 这取决 
于编译时编译器认为会使用那些分支。 因为这个原因, 很多程序员不喜欢采用这个特性, 并且 
专门地使用-fno-guess-branch-probability选项关闭这个特性
-fcprop-registers: 因为在函数中把寄存器分配给变量, 所以编译器执行第二次检查以便减少 
调度依赖性(两个段要求使用相同的寄存器)并且删除不必要的寄存器复制操作。

第二级:新的视角 
新的视角强调的重点是针对问题的算法;即选择和构造适合于问题的算法;(冒泡排序还是快排的选择问题是这一级早就应该完成的)很多经典算法都对问题作了一些假设(包括我们当前已经完成的算法实现),而在面对实际问题时“新的视角”提示我们应该重新检视这些假设,并尝试不同的思考问题的角度,寻求适合于问题的新算法; 
发掘问题的本来意义,从不同的角度思考面对的问题,使用适合于问题的的算法; 尝试打破一些规则,发掘和怀疑自己的某些假定,恢复问题的本来面目;
结合了第一个级别的所有优化技术, 再加上一下一些优化: 
-fforce-mem: 这种优化再任何指令使用变量前, 强制把存放再内存位置中的所有变量都复制到寄存器 
中。 对于只涉及单一指令的变量, 这样也许不会有很大的优化效果. 但是对于再很多指令(必须数学操作) 
中都涉及到的变量来说, 这会时很显著的优化, 因为和访问内存中的值相比 ,处理器访问寄存器中的值要 
快的多。
-foptimize-sibling-calls: 这种技术处理相关的和/或者递归的函数调用。 通常, 递归的函数调用 
可以被展开为一系列一般的指令, 而不是使用分支。 这样处理器的指令缓存能够加载展开的指令并且 
处理他们, 和指令保持为需要分支操作的单独函数调用相比, 这样更快。
-fstrength-reduce: 这种优化技术对循环执行优化并且删除迭代变量。 迭代变量是捆绑到循环计数器 
的变量, 比如使用变量, 然后使用循环计数器变量执行数学操作的for-next循环。
-fgcse: 这种技术对生成的所有汇编语言代码执行全局通用表达式消除历程。 这些优化操作试图分析 
生成的汇编语言代码并且结合通用片段, 消除冗余的代码段。如果代码使用计算性的goto, gcc指令推荐 
使用-fno-gcse选项。
-fcse-follow-jumps: 这种特别的通用子表达式消除技术扫描跳转指令, 查找程序中通过任何其他途径都不 
会到达的目标代码。 这种情况最常见的例子就式if-then-else语句的else部分。
-frerun-cse-after-loop: 这种技术在对任何循环已经进行过优化之后重新运行通用子表达式消除例程。 
这样确保在展开循环代码之后更进一步地优化还编代码。
-fdelete-null-pointer-checks: 这种优化技术扫描生成的汇编语言代码, 查找检查空指针的代码。编译 
器假设间接引用空指针将停止程序。 如果在间接引用之后检查指针, 它就不可能为空。
-fextensive-optimizations: 这种技术执行从编译时的角度来说代价高昂的各种优化技术,但是它可能 
对运行时的性能产生负面影响。
-fregmove: 编译器试图重新分配mov指令中使用的寄存器, 并且将其作为其他指令操作数, 以便最大化 
捆绑的寄存器的数量。
-fschedule-insns: 编译器将试图重新安排指令, 以便消除等待数据的处理器。 对于在进行浮点运算时有 
延迟的处理器来说, 这使处理器在等待浮点结果时可以加载其他指令。
-fsched-interblock: 这种技术使编译器能够跨越指令块调度指令。 这可以非常灵活地移动指令以便等待 
期间完成的工作最大化。
-fcaller-saves: 这个选项指示编译器对函数调用保存和恢复寄存器, 使函数能够访问寄存器值, 而且不必 
保存和恢复他们。 如果调用多个函数, 这样能够节省时间, 因为只进行一次寄存器的保存和恢复操作, 而 
不是在每个函数调用中都进行。
-fpeephole2: 这个选项允许进行任何计算机特定的观察孔优化。
-freorder-blocks: 这种优化技术允许重新安排指令块以便改进分支操作和代码局部性。
-fstrict-aliasing: 这种技术强制实行高级语言的严格变量规则。 对于c和c++程序来说, 它确保不在数据 
类型之间共享变量. 例如, 整数变量不和单精度浮点变量使用相同的内存位置。
-funit-at-a-time: 这种优化技术指示编译器在运行优化例程之前读取整个汇编语言代码。 这使编译器可以 
重新安排不消耗大量时间的代码以便优化指令缓存。 但是, 这会在编译时花费相当多的内存, 对于小型计算机可能 
是一个问题。
-falign-functions: 这个选项用于使函数对准内存中特定边界的开始位置。大多数处理器按照页面读取内存, 
并且确保全部函数代码位于单一内存页面内, 就不需要叫化代码所需的页面。
-fcrossjumping: 这是对跨越跳转的转换代码处理, 以便组合分散在程序各处的相同代码。 这样可以减少 
代码的长度, 但是也许不会对程序性能有直接影响。

第三级:表驱动状态机 
将问题抽象为另一种等价的数学模型或假想机器模型,比如构造出某种表驱动状态机;这一级其实是第二级的延伸,只是产生的效果更加明显,但它有其本身的特点(任何算法和优化活动都可以看作是他的投影);这一级一般可以产生无与伦比的快速程序, 要达到这一级需要大量修炼的;并且思考时必须放弃很多已有的概念或者这些概念不再重要,比如:变量、指针、空间、函数、对象等,剩下的只应该是那个表驱动状态机; 我想把这种境界描述为:空寂中,一些输入驱动着一个带有状态的机器按设定好的最短路线运转着;除此之外have nothing; 既:把解决一个问题的算法看作一个机器,它有一些可变的状态、有一些记忆、有一些按状态运行的规则,然后一些输入驱动这个机器运转;这就是第三级要求的思考优化问题的切入点,也就是寻找一部机器,使它运行经过的路径最短(可能是速度也可能是空间等等)
它整合了第一和第二级别中的左右优化技巧, 还包括一下优化: 
-finline-functions: 这种优化技术不为函数创建单独的汇编语言代码,而是把函数代码包含在调度程序的 
代码中。 对于多次被调用的函数来说, 为每次函数调用复制函数代码。 虽然这样对于减少代码长度不利, 但是 
通过最充分的利用指令缓存代码, 而不是在每次函数调用时进行分支操作, 可以提高性能。
-fweb: 构建用于保存变量的伪寄存器网络。 伪寄存器包含数据, 就像他们是寄存器一样, 但是可以使用各种 
其他优化技术进行优化, 比如cse和loop优化技术。
-fgcse-after-reload: 这中技术在完全重新加载生成的且优化后的汇编语言代码之后执行第二次gcse优化, 
帮助消除不同优化方式创建的任何冗余段。
<think>我们正在讨论GCC编译器的优化选项及使用方法。根据提供的引用资料,我们可以总结出以下关键点:1.GCC提供了优化选项,可以通过命令行参数进行控制。2.常见的优化等级包括-O0(无优化)、-O1、-O2、-O3(最高优化等级)等。3.除了整体优化等级外,GCC还提供许具体的优化选项,例如-fomit-frame-pointer(省略帧指针)。4.在S32DS开发环境中,可以通过工程属性设置优化等级。根据这些信息,我们可以为用户提供GCC优化选项的详细说明和使用方法。###GCC优化选项及使用方法GCC编译器提供了层次的优化选项,主要分为**优化等级**和**具体优化选项**两类。####1.优化等级(最常用)-**-O0**:关闭所有优化。这是默认选项,适用于调试阶段,因为生成的代码与源代码结构一致,便于调试。-**-O1**或**-O**:基础优化。编译器会尝试减少代码大小和执行时间,但不会进行耗时较长的优化。-**-O2**:更高级的优化。包括处理器指令调度、循环优化等,通常推荐在发布版本中使用。-**-O3**:最高级优化。在-O2基础上增加更优化,如函数内联、循环展开等,但可能增加代码大小。-**-Os**:优化代码大小。在-O2的基础上,选择那些不会显著增加代码大小的优化。-**-Ofast**:执行所有-O3优化,并启用一些不严格遵循标准的优化(如浮点运算的激进优化)。使用示例:```bashgcc-O2-omy_programmy_program.c```####2.常用具体优化选项除了整体优化等级,还可以单独启用或禁用某些优化:-**-fomit-frame-pointer**:省略帧指针(framepointer),可以释放一个寄存器(通常为EBP/RBP),可能提升性能[^1]。-**-funroll-loops**:展开循环,减少循环开销,但可能增加代码大小。-**-finline-functions**:对认为简单的函数进行内联扩展。-**-march=native**:生成针对当前主机处理器架构优化的代码(使用主机支持的所有指令集)。使用示例(组合个选项):```bashgcc-O2-fomit-frame-pointer-march=native-omy_programmy_program.c```####3.如何检测优化效果-**查看汇编代码**:使用`-S`选项生成汇编代码,便于分析编译器优化行为。```bashgcc-O2-S-omy_program.smy_program.c```-**使用性能分析工具**:如`perf`(Linux)或`gprof`,对比优化前后的程序性能。####4.在集成开发环境(IDE)中设置优化选项以S32DS为例,设置优化等级的方法[^3]:1.右键单击工程中的源文件。2.选择“属性”(Properties)。3.导航到:C/C++Build->Settings->StandardS32DSCCompiler->Optimization。4.在“OptimizationLevel”下拉菜单中选择所需优化等级(如-O2)。####注意事项-高优化等级(如-O3)可能增加编译时间,且不一定在所有情况下都能提升性能。-某些优化可能影响调试,因此在开发阶段建议使用-O0或-O1。-激进优化(如-Ofast)可能改变浮点运算行为,在科学计算中需谨慎使用。###总结GCC优化选项的使用需要根据具体场景权衡。通常,开发调试阶段使用-O0,发布版本使用-O2。特殊需求(如最小代码大小)则选择-Os。此外,可以结合具体选项进行微调。建议通过性能测试确定最佳优化方案。§§相关问题§§1.GCC优化选项-O1、-O2、-O3的具体区别是什么?2.如何避免GCC优化带来的副作用(如调试困难)?3.GCC优化选项对嵌入式系统开发有何特殊考虑?4.如何为特定处理器架构(如ARMCortex-M)优化代码?</think>### GCC优化选项及使用方法详解 GCC(GNU Compiler Collection)的优化选项可显著提升代码性能,以下是核心选项和使用方法: #### 一、优化等级(最常用) 1. **-O0**(默认) - 关闭所有优化,保留调试信息 - 编译速度快,适合开发调试 ```bash gcc -O0 -o program source.c ``` 2. **-O1 / -O**(基础优化) - 减少代码体积和执行时间 - 包含跳转优化、常量传播等 ```bash gcc -O1 -o program source.c ``` 3. **-O2**(推荐发布级别)⭐ - 包含循环优化、指令调度等 - 平衡性能与代码大小 ```bash gcc -O2 -o program source.c ``` 4. **-O3**(激进优化) - 增加函数内联、循环展开等 - 可能增加代码体积 ```bash gcc -O3 -o program source.c ``` 5. **-Os**(优化代码大小) - 在-O2基础上优先减小体积 - 适合嵌入式系统 ```bash gcc -Os -o program source.c ``` #### 二、关键子选项 1. **-fomit-frame-pointer** - 省略栈帧指针(释放EBP/RBP寄存器) - 提升性能但影响调试[^1] ```bash gcc -O2 -fomit-frame-pointer ... ``` 2. **-march=native** - 针对当前CPU架构优化 ```bash gcc -O2 -march=native ... ``` 3. **-funroll-loops** - 循环展开(需配合-O3) ```bash gcc -O3 -funroll-loops ... ``` #### 三、IDE配置示例(Code::Blocks/S32DS) 1. **Code::Blocks** ```plaintext Project → Build options → Compiler settings → Other options 添加 -O2 -march=native ``` 2. **S32DS**(NXP开发环境) ```plaintext 右键源文件 → Properties → C/C++ Build → Standard S32DS C Compiler → Optimization → 选择优化等级[^3] ``` #### 四、优化效果验证 1. 查看汇编代码: ```bash gcc -O2 -S -o output.s input.c ``` 2. 性能分析工具: ```bash perf stat ./program # Linux性能计数器 gprof ./program # 函数级性能分析 ``` #### 五、注意事项 1. 调试时建议使用 **-O0 -g** 2. 高优化等级可能暴露未定义行为 3. 嵌入式系统需注意 **-Os** 与栈溢出风险 4. 旧版GCC需验证选项兼容性[^2] > **最佳实践**:发布版本建议组合使用 > ```bash > gcc -O2 -march=native -fomit-frame-pointer -pipe > ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值