优化级别-O3在g++中是否危险

Is optimisation level -O3 dangerous in g++?

本文关键字:是否 危险 g++ -O3 优化      更新时间:2023-10-16

我从各种来源(尽管大多来自我的一位同事)听说,在g++中以-O3的优化级别进行编译在某种程度上是"危险的",除非被证明是必要的,否则通常应该避免。

这是真的吗?如果是,为什么?我应该坚持-O2吗?

在gcc的早期(2.8等)和egcs时代,redhat 2.96-O3有时会有相当大的缺陷。但这是十多年前的事情了,-O3与其他级别的优化(在bug中)没有太大区别。

然而,它确实倾向于揭示人们依赖未定义行为的情况,因为人们更严格地依赖语言的规则,尤其是角落案例。

作为个人注意,我在金融部门使用-O3运行生产软件多年了,还没有遇到过如果我使用-O2就不会出现的错误。

根据大众需求,这里有一个补充:

-O3,尤其是像-funroll循环(未被-O3启用)这样的附加标志有时会导致生成更多的机器代码。在某些情况下(例如,在具有异常小的L1指令缓存的cpu上),这可能会导致速度减慢,因为例如,某些内部循环的所有代码现在不再适合L1I。一般来说,gcc会尽量不生成太多代码,但由于它通常会优化泛型情况,因此可能会发生这种情况。特别容易出现这种情况的选项(如循环展开)通常不包括在-O3中,并在手册页中进行相应标记。因此,通常使用-O3来生成快速代码是一个好主意,并且只有在适当的时候(例如,当探查器指示L1I未命中时)才返回-O2或-Os(尝试针对代码大小进行优化)。

如果你想把优化发挥到极致,你可以在gcc中通过--param调整与某些优化相关的成本。此外,请注意,gcc现在可以将属性放在仅控制这些函数的优化设置的函数中,因此,当您发现一个函数中的-O3有问题时(或者只想尝试该函数的特殊标志),您不需要使用O2编译整个文件,甚至整个项目。

otoh似乎在使用-Oast时必须小心,它指出:

-Ofast支持所有-O3优化。它还启用了并非对所有标准都有效的优化合规程序。

这让我得出结论,-O3旨在完全符合标准。

根据我的经验,将-O3应用于整个程序几乎总是会使其速度变慢(相对于-O2),因为它会开启激进的循环展开和内联,使程序不再适合指令缓存。对于较大的程序,相对于-Os-O2也是如此!

-O3的预期使用模式是,在分析程序后,手动将其应用于少数包含关键内部循环的文件,这些文件实际上受益于这些积极的速度权衡空间。GCC的较新版本具有概要文件引导的优化模式,该模式可以(IIUC)选择性地将-O3优化应用于热函数,从而有效地自动化了这一过程。

是的,O3有缺陷。我是一名编译器开发人员,在构建自己的软件时,我发现了由O3生成有缺陷的SIMD汇编指令引起的明显的gcc错误。据我所见,大多数生产软件都附带了O2,这意味着O3在测试和错误修复方面不会受到太多关注。

这样想:O3在O2上增加了更多的转化,这在O1上增加了更大的转化。从统计数据来看,更多的转换意味着更多的错误。任何编译器都是如此。

-O3选项除了启用较低级别"-O2"answers"-O1"的所有优化外,还启用了更昂贵的优化,如函数内联。"-O3"优化级别可以提高生成的可执行文件的速度,但也可以增加其大小。在下面在某些情况下,如果这些优化效果不佳,则此选项实际上可能会使程序速度变慢。

最近我在使用g++进行优化时遇到了一个问题。该问题与PCI卡有关,其中寄存器(用于命令和数据)由内存地址表示。我的驱动程序将物理地址映射到应用程序中的一个指针,并将其提供给被调用的进程,该进程的工作方式如下:

unsigned int * pciMemory;
askDriverForMapping( & pciMemory );
...
pciMemory[ 0 ] = someCommandIdx;
pciMemory[ 0 ] = someCommandLength;
for ( int i = 0; i < sizeof( someCommand ); i++ )
    pciMemory[ 0 ] = someCommand[ i ];

这张卡片没有按预期操作。当我看到汇编代码时,我明白编译器只将someCommand[ the last ]写入pciMemory,省略了前面的所有写入。

总之:要准确和专注于优化。