KEIL5优化等级详细介绍

3.4选择优化选项

Arm®编译器执行了一些优化,以减少代码大小并提高应用程序的性能。有不同的优化层次,它们有不同的优化目标。因此,优化某个目标会对其他目标产生影响。优化水平总是这些不同目标之间的一种权衡。

ARM编译器提供了不同的优化级别来控制不同的优化目标。针对您的应用程序的最佳优化级别取决于您的应用程序和优化目标。

表3-8优化目标

优化目标

有用的优化级别

较小的代码大小

-Oz, -Omin

更快的性能

-O2, -O3, -Ofast, -Omax

良好的调试经验,没有代码膨胀

-O1

源代码和生成代码之间更好的相关性

-O0(无优化)

更快的编译和构建时间

-O0(无优化)

平衡的代码大小的减少和快速的性能

-Os

如果您使用更高的优化级别来实现性能,那么它会对其他目标有更高的影响,如降级的调试体验、增加的代码大小和增加的构建时间。

如果您的优化目标是减少代码大小,那么它会对其他目标产生影响,如调试体验下降、性能下降和增加生成时间等。

armclang提供了一系列选项,以帮助您找到适合您的需求的方法。考虑一下是减少代码大小还是提高性能是对应用程序最重要的目标,然后选择一个匹配您的目标的选项。

优化级别-O0

-O0禁用所有优化。此优化级别是默认的优化级别。使用-O0可以产生更快的编译和构建时间,但产生的代码比其他优化级别要慢。在-O0时,代码大小和堆栈使用量明显高于其他优化级别。生成的代码与源代码密切相关,但明显生成了更多的代码,包括死代码。

优化级别-O1

-O1支持在编译器中的核心优化。这个优化级别提供了良好的调试体验,具有比-O0更好的代码质量。此外,堆栈的使用情况也提高了超过-O0。Arm推荐此选项,以获得良好的调试体验。

使用-O1与-O0时的差异在于:

已启用了优化,这可能会降低调试信息的保真度。

内联和尾调用被启用,这意味着回溯可能不会提供读取源代码的开放函数激活堆栈。

如果不需要该结果,则可能不会在预期的位置调用没有副作用的函数,或者可能被省略。

变量的值可能在其范围内不可用。例如,它们的堆栈位置可能已经被重用了。

优化等级-O2

与-O1相比,-O2是一个更高的性能优化方法。它增加了一些新的优化,并改变了与-O1相比用于优化的启发式。这个级别是编译器可能自动生成向量指令的第一个优化级别。它还会降低调试体验,并可能导致比-O1增加的代码大小。

使用-O2与使用-O1时的差异是:

编译器认为内联一个调用站点是有利可图的阈值可能会增加。

所执行的循环展开的量可能会增加。

可以为简单的循环和独立标量操作的相关序列生成向量指令。

可以使用armclang命令行选项-fno-vectorize来抑制矢量指令的创建。

优化等级-O3

与o2相比,o3是一个更高的性能优化方法。这种优化级别支持需要大量编译时分析和资源的优化,并与-O2相比更改了优化的启发式。-O3指示编译器优化生成代码的性能,而忽略生成代码的大小,这可能会导致代码大小的增加。与-O2相比,它还会降低了调试体验。

使用O3与使用O2时的差异是:

编译器认为内联一个调用站点是有利可图的阈值会增加。

所执行的循环展开的量将会增加。

在编译器管道的后期启用了更积极的指令优化。

优化级别-Os

-Os旨在在不显著增加代码大小的情况下提供高性能。根据您的应用程序,-Os提供的性能可能类似于-O2或-O3。

与-O3相比,Os提供了代码大小的减少。与-O1相比,它还降低了调试体验。

使用Os与O3时的差异是:

编译器认为内联调用站点是有利可图的阈值会降低。

所执行的循环展开量显著降低。

优化水平-Oz

-Oz的目标是提供减少的代码大小,而不使用链路时间优化(LTO)。如果LTO不适合您的应用程序,Arm建议使用此选项以获得最佳代码大小。与-O1相比,这个优化级别降低了调试体验。

使用-Oz与-Os时的差异在于:

指示编译器只对代码大小进行优化,而忽略性能优化,这可能会导致代码变慢。

未禁用函数内联。在某些情况下,内联可能会减少代码大小,例如函数只调用一次。只有当代码大小预期会减少时,内嵌启发式才会调整为内联。

将禁用可能会增加代码大小的优化,如循环展开和循环向量化。

循环作为时循环生成,而不是执行时循环生成。

优化级别-Omin

-Omin旨在提供尽可能小的代码大小。Arm建议使用此选项,以获得最佳的代码大小。与-O1相比,这个优化级别降低了调试体验。

使用-Omin与-Oz时的差异是:

-Omin支持一组基本的链接时间优化(LTO),旨在删除未使用的代码和数据,同时也试图优化全局内存访问。

-Omin使虚拟功能消除,这是C++用户特别受益的一个好处。

如果您想在-Omin上编译并使用单独的编译和链接步骤,那么您还必须在armlink命令行中包含-Omin。

优化水平-Ofast

-Ofast从o3级执行优化,包括使用fammamclang选项执行的优化。

此级别还执行其他可能违反严格遵守语言标准的积极优化。

这个级别会降低调试体验,并可能导致比-O3增加的代码大小。

优化级别-Omax

-Omax执行最大限度的优化,并专门针对性能优化。它支持来自级别ofast的所有优化,以及链路时间优化(LTO)。

在这个优化级别上,ARM编译器可能会违反严格的语言标准。使用此优化级别以获得最快的性能。

此级别会降低调试体验,并可能导致比-Ofast有更大的代码大小。

如果您想在-Omax上编译,并且有单独的编译和链接步骤,那么您还必须在armlink命令行中包含-Omax。

示例

该示例显示了在使用-O0优化选项时进行的代码生成。若要执行此优化,请使用以下方法编译源文件:

目标=手ARM,目标=文件

表3-9使用-O0生成代码的示例

file.c中的源代码

未优化的输出

int dummy()

{

    int x=10, y=20;

    int z;

    z=x+y;

    return 0;

}

dummy:

    .fnstart

    .pad #12

     sub     sp, sp, #12

     mov     r0, #10

     str     r0, [sp, #8]

     mov     r0, #20

     str     r0, [sp, #4]

     ldr     r0, [sp, #8]

     add     r0, r0, #20

     str     r0, [sp]

     mov     r0, #0

     add     sp, sp, #12

     bx  lr

该示例显示了在使用-O1优化选项时进行的代码生成。若要执行此优化,请使用以下方法编译源文件:

armclang --target=arm-arm-none-eabi -march=armv7-a -O1 -S file.c

表3-10使用-O1生成代码的示例

file.c中的源代码

从armclang优化输出

int dummy()

{

    int x=10, y=20;

    int z;

    z=x+y;

    return 0;

}

dummy:

    .fnstart

    movs r0, #0

    bx lr

源文件大多包含死代码,如int x=10和z=x+y。在优化级别o0,编译器不执行优化,因此为源文件中的死代码生成代码。但是,在优化级别o1中,编译器不会为源文件中的死代码生成代码。

### Keil 5 中不同调试优化等级的设置方法及影响 #### 设置方法 在 Keil 5 中,可以通过以下方式设置优化等级: 1. 打开项目后,在 **Project** 菜单中选择 **Options for Target 'Target Name'**[^1]。 2. 进入 **C/C++** 配置页面,找到 **Optimize** 下拉菜单。这里可以选择 `Level0` 至 `Level3` 的优化级别,分别对应 `-O0`, `-O1`, `-O2`, 和 `-O3` 编译器参数[^2]。 3. 如果需要手动指定编译器命令行参数,则可以在同一界面中的 **Misc Controls** 输入框内输入类似 `-Onum` 的指令来设定优化等级。 对于初学者或者需要在线调试的情况,推荐使用 `Level0`(即无优化),因为此时生成的目标代码与源码逻辑完全一致,便于理解程序行为并设置断点。然而当切换到更高优化等级时,可能会遇到某些位置无法成功插入断点的现象,这是因为高度优化后的代码结构已发生改变[^5]。 #### 对程序性能的影响 随着优化级别的提升,目标代码的各项特性会发生如下变化: - **运行效率**: 更高的优化等级通常意味着更快的执行速度,这是由于编译器会对算法实现做出改进,减少不必要的操作步骤或重复计算。 - **代码尺寸**: 尽管高级别的优化有助于压缩最终可执行文件大小,但在特定情况下也可能因展开函数调用等原因反而增大了整体体积。 - **调试体验**: 当启用较高程度的优化之后,原始变量值可能不再准确反映实际状态;甚至部分语句会被移除或重组,从而干扰正常的跟踪分析过程[^3]。 - **编译耗时**: 实现复杂转换所需的工作量增加必然延长整个构建流程所花费的时间长度[^4]。 值得注意的是,在进行精确计时测试(如测量浮点数运算耗时)时发现经过充分优化处理过的版本往往展现出显著优于未加工前的表现水平。 ```c // 示例:简单的浮点加法演示 float a = 1.0f; float b = 2.0f; void main() { float result = add(a, b); } inline float add(float x, float y){ return x + y; // 此处简单表达式很可能被替换为直接求和的结果 } ``` 上述例子展示了即使是最基础的操作也有可能受到不同程度优化措施的作用效果差异对比情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值