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

时间:2012-07-18 16:29:58

标签: c++ optimization g++ compiler-flags

我从各种来源(虽然大部分来自我的同事)都听说过,用g ++中的-O3优化级别进行编译在某种程度上是“危险的”,一般情况下应该避免,除非证明是必要的

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

5 个答案:

答案 0 :(得分:199)

在gcc(2.8等)的早期以及在egcs时代,redhat 2.96 -O3有时候很吵。但这是十多年前的事了,而-O3与其他优化水平(在马车中)并没有多大差别。

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

作为一个个人说明,我使用-O3在金融领域运行生产软件多年,并且还没有遇到如果我使用-O2就不会出现的错误。

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

-O3,特别是像-funroll-loops(未由-O3启用)等附加标志有时会导致生成更多的机器代码。在某些情况下(例如,在具有异常小的L1指令高速缓存的cpu上),这可能导致由于例如所有代码的所有代码而减速。一些内环现在不再适合L1I。通常gcc很难不生成如此多的代码,但由于它通常会优化通用情况,因此可能会发生这种情况。特别容易出现这种情况的选项(如循环展开)通常不包含在-O3中,并在联机帮助页中相应标记。因此,通常最好使用-O3生成快速代码,并在适当时(例如,当分析器指示L1I未命中时)仅回退到-O2或-Os(尝试优化代码大小)。

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

otoh似乎在使用-Ofast时必须小心,其中指出:

  

-Ofast启用所有-O3优化。   它还支持对所有标准无效的优化   合规计划。

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

答案 1 :(得分:35)

这已经在Neel的回答中说过,但不够明显或强烈:

在我有点格格不入的经历中,将-O3应用于整个程序几乎总是会使其变慢(相对于-O2),因为它会启用积极的循环展开和内联使程序不再适合指令缓存。对于较大的程序,-O2相对于-Os

也是如此

-O3的预期用途模式是,在对程序进行概要分析后,您手动将其应用于少数几个包含关键内部循环的文件,这些文件实际上受益于这些激进的空间 - 速度权衡。对于最近的GCC,我认为闪亮的新链接时间配置文件引导优化模式可以有选择地将-O3优化应用于热门功能 - 有效地自动化此过程。

答案 2 :(得分:8)

除了较低级别'-O2'和'-O1'的所有优化之外,

-O3选项还会启用更昂贵的优化,例如函数内联。 '-O3'优化级别可以提高生成的可执行文件的速度,但也可以增加其大小。下 在某些情况下,这些优化不利,这个选项实际上可能会使程序变慢。

答案 3 :(得分:5)

是的,O3很吵。我是一名编译器开发人员,我已经确定了在构建我自己的软件时由O3生成错误的SIMD汇编指令引起的明显且明显的gcc错误。从我所看到的情况来看,大多数生产软件都带有O2,这意味着O3在测试和错误修复方面的关注度会降低。

以这种方式思考:O3在O2之上添加了更多转换,这在O1之上添加了更多转换。从统计学上讲,更多的转换意味着更多的错误。这对任何编译器都是如此。

答案 4 :(得分:4)

最近我在使用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,省略了所有先前的写入。

总之:准确,专注于优化。