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中,编译器不会为源文件中的死代码生成代码。