Optimization 为什么要用-O2而不是-O3编译

Optimization 为什么要用-O2而不是-O3编译,optimization,g++,Optimization,G++,我们通常使用-O2进行编译,因为-O3会“触发细微的bug” 对于我们的GCC版本,-O3支持更积极的内联,这实际上会暴露未被注意到的bug(例如,使用函数中的未初始化值作为引用参数或数组的越界访问)。在我看来,这种积极的内联还允许使用更小的函数和-funswitch循环进行更具表现力的编码,并且有助于使变量定义在循环中更局部 考虑到我们的代码中的bug比编译器bug的可能性大几个数量级,并且我们使用-Wall-Wextra时没有任何问题,我们应该寻找哪种bug 如果有必要,我们使用gcc-4.

我们通常使用
-O2
进行编译,因为
-O3
会“触发细微的bug”

对于我们的GCC版本,
-O3
支持更积极的内联,这实际上会暴露未被注意到的bug(例如,使用函数中的未初始化值作为引用参数或数组的越界访问)。在我看来,这种积极的内联还允许使用更小的函数和
-funswitch循环进行更具表现力的编码,并且
有助于使变量定义在循环中更局部

考虑到我们的代码中的bug比编译器bug的可能性大几个数量级,并且我们使用
-Wall-Wextra
时没有任何问题,我们应该寻找哪种bug


如果有必要,我们使用
gcc-4.3.2
。编译时间对我们来说不是一个主要问题。

有时候,像您提到的那样,激进的优化可能会破坏代码。如果这是您当前正在进行的项目,那么这可能不是问题。然而,如果所讨论的代码是脆弱的、写得不好的、不易理解的遗留代码,那么您希望尽可能少地冒险

此外,并非所有优化都经过正式验证。这意味着它们可能以不受欢迎的方式改变程序的行为

我能想到的最好的例子是Java,但它应该说明我关于一般优化的观点

有这样的代码是很常见的

 while( keepGoing ){
      doStuff();
 }
然后另一个线程修改
keepGoing
的值。JVM将要做的一个优化是看到
keepGoing
不会在循环体中被修改,因此它会“提升”它并在循环之前进行检查,基本上将代码转换为:

 if( keepGoing ){
      while( true ){
           doStuff();
      }
 }
这在多线程环境中不是一回事,但在单线程环境中却是一回事。这些都是可以与优化决裂的事情。这是一个经常使用的名词


PS-在Java中,正确的答案是make
keepGoing
volatile
”,因此它不能假定缓存的值,并且可以执行您想要的操作。

大小。当然,如果大小真的很重要(有时是这样,比如嵌入式),人们会使用
-Os
。但O3的主要区别在于(您已经提到)内联。这可以增加生成的代码大小(但速度更快)。也许你想要速度,但不是以空间为代价?否则我看不出有什么理由不使用O3(除非你知道一个gcc编译器错误只发生在O3的代码中,但只要你没有错误,你就不能在O2上复制,我不在乎)。

不要自欺欺人,编译器错误不会潜伏在那里,让你的生活陷入地狱。这是一个去年在Debian出现的问题,修复的方法是返回到-O2

这确实是一个包含大量遗留代码的项目。我们使用
保险
来确保温暖和模糊的感觉。该代码具有非常可怕的缓存和分支预测失误问题,这些问题似乎在
-O3
上得到了缓解。我只是想知道到底该注意什么。除了我们自己的迷信之外,你还有什么经验吗?这篇博文让我厌倦了优化:我能想到的最好的例子就是Java。有这样的代码是很常见的,我编辑了我的答案,其中包括了一个可能发生的优化实例,并改变了代码的行为。实际上,根据缓存大小内联可能会使代码从缓存未命中内联中变慢。@ArtB验证缓存大小不是编译器的责任吗在它编译的平台上,是否有足够大的空间来实现它应该执行的内联程度?还是我期望太高?@VF1 x86的缓存大小是多少?缓存大小取决于系统,编译器不一定知道…谢谢,这非常有用。我不知道我们是否应该继续使用
-O3
,直到我们遇到弗洛洛建议的任何问题。如果一个bug能产生如此明显错误的结果,那就更糟了。