为啥要使用 -O2 而不是 -O3 进行编译
Posted
技术标签:
【中文标题】为啥要使用 -O2 而不是 -O3 进行编译【英文标题】:Why would one ever want to compile with -O2 instead of -O3为什么要使用 -O2 而不是 -O3 进行编译 【发布时间】:2011-04-12 15:35:19 【问题描述】:我们通常使用-O2
进行编译,因为-O3
会“触发细微的错误”。
对于我们的 GCC 版本,-O3
启用了更积极的内联,这实际上会揭示否则未被注意到的错误(例如,使用来自函数的未初始化值将它们作为引用参数或数组的越界访问)。在我看来,这种激进的内联还允许使用更小的函数以更具表现力的方式进行编码,-funswitch-loops
有助于使变量定义在循环中更加本地化。
鉴于我们代码中的错误比编译器错误高几个数量级,并且我们使用-Wall -Wextra
没有任何问题,我们应该寻找什么样的错误?
如果重要,我们使用gcc-4.3.2
。编译时间对我们来说不是主要问题。
【问题讨论】:
【参考方案1】:尺寸。当然,如果大小确实很重要(有时很重要,比如嵌入式),那么可以使用-Os
。但 O3 的主要区别在于(您已经提到过)内联。这可以增加生成的代码大小(但速度更快)。也许您想要速度,但根本不想要(空间)成本?否则我认为没有理由不使用 O3(除非您知道 gcc 编译器错误仅出现在您的 O3 代码中,但只要您没有错误,您就无法在 O2 重现,我不在乎) .
【讨论】:
实际上,根据缓存大小,内联可能会使代码变慢,因为内联需要缓存未命中。 @ArtB 编译器是否有责任验证它正在编译的平台上的缓存大小是否足够大,可以满足它应该执行的内联程度?还是我期待太多? @VF1 x86 的缓存大小是多少?缓存大小取决于系统,编译器不一定知道...【参考方案2】:不要自欺欺人,编译器错误不会潜伏在那里让你的生活陷入困境。这是去年在 Debian 中出现的 nasty one,修复是回退到 -O2。
【讨论】:
谢谢,这很有用。我想知道我们是否应该继续使用-O3
,直到我们遇到 flolo 建议的任何问题。如果一个错误会产生如此明显的错误结果,那就更糟了。【参考方案3】:
有时激进的优化可能会破坏代码,就像您提到的那样。如果这是您当前正在从事的项目,那么也许这不是问题。但是,如果有问题的代码是脆弱、编写不佳且未被充分理解的遗留代码,那么您希望尽可能少地冒险。
此外,并非所有优化都经过正式证明。这意味着它们可能会以不受欢迎的方式改变程序的行为。
我能想到的最好的例子是 Java 的例子,但它应该能说明我对一般优化的观点。
这样的代码很常见
while( keepGoing )
doStuff();
然后keepGoing
的值被另一个线程修改。 JVM 将进行的一项优化是看到 keepGoing
在循环体中没有被修改,因此它“提升”它并检查 before 循环,本质上将代码转换为:
if( keepGoing )
while( true )
doStuff();
这在多线程环境中不是一回事,但在单线程环境中却是。这些是可以通过优化打破的东西。这是“Heisenbugs”中频繁出现的source。
PS- 在 Java 中,正确的答案是 make keepGoing
"volatile
" 所以它不能假定缓存的值并且会做你想做的事情。
【讨论】:
这确实是一个包含大量遗留代码的项目。我们使用insure
来获得温暖和模糊的感觉。该代码具有非常可怕的缓存和分支预测错误问题,这些问题似乎在-O3
得到了缓解。我只是想知道要明确注意什么。除了我们自己的迷信之外,你有什么经验吗?
这篇博文让我厌倦了优化:esr.ibiblio.org/?p=1705 我能想到的最好的例子是 Java 的。在哪里有这样的代码是很常见的
我编辑了我的答案以包含一个可能发生的优化实例并改变代码的行为。以上是关于为啥要使用 -O2 而不是 -O3 进行编译的主要内容,如果未能解决你的问题,请参考以下文章
为啥我的代码可以在 chrome 上运行,而不是一次使用 cordova 编译