g++中的优化级别-O3危险吗?

我从各种来源(虽然主要是从我的一个同事那里)听说,在g++中使用-O3的优化级别进行编译在某种程度上是“危险的”,除非被证明是必要的,否则一般应该避免。

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

151623 次浏览

在gcc(2.8等)的早期,在egcs和redhat 2.96的时代,-O3有时很有bug。但这是十多年前的事情了,-O3与其他级别的优化(在bug方面)并没有太大的不同。

然而,它确实倾向于揭示人们依赖于未定义的行为的情况,由于更严格地依赖于语言的规则,特别是边缘情况。

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

应大众要求,在此增加一项:

-O3,特别是像-funroll-loops这样的附加标志(-O3不启用)有时会导致生成更多的机器代码。在某些情况下(例如在一个L1指令缓存非常小的cpu上),这可能会导致由于所有的代码(例如一些内部循环现在不再适合L1I)而导致放缓。通常gcc会尽量不生成这么多代码,但由于它通常会优化泛型情况,所以这种情况可能会发生。特别容易出现这种情况的选项(如循环展开)通常不包含在-O3中,并在manpage中相应地标记。因此,使用-O3来生成快速代码通常是一个好主意,只有在适当的时候(例如,当分析器指出L1I遗漏时)才返回到-O2或-Os(试图优化代码大小)。

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

otoh似乎在使用-Ofast时必须小心,它表示:

-Ofast启用所有-O3优化。 它还可以实现并非对所有标准都有效的优化 兼容的程序。< / p >

这让我得出结论-O3是完全符合标准的。

-O3选项除了所有较低级别' -O2 '和' -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,省略了所有前面的写入。

总之:在优化时要做到准确和细心。

在我的经验中,将-O3应用到整个程序几乎总是会使它更慢(相对于-O2),因为它会打开主动循环展开和内联,使程序不再适合指令缓存。对于较大的程序,对于-O2相对于-Os!

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

是的,O3更麻烦。我是一名编译器开发人员,在构建自己的软件时,我已经确定了由O3生成有bug的SIMD汇编指令引起的清晰而明显的gcc错误。据我所知,大多数产品软件都带有O2,这意味着O3在测试和错误修复方面得到的关注较少。

可以这样想:O3在O2上加了更多的变换,O2在O1上加了更多的变换。从统计学上讲,更多的转换意味着更多的bug。这对任何编译器都适用。