我什么时候可以自信地用-O3编译程序?

时间:2013-02-13 09:48:27

标签: c++ gcc compiler-construction compilation

我见过很多人抱怨-O3选项:

GCC: program doesn't work with compilation option -O3

Floating Point Problem provided by David Hammen

我查看GCC的手册:

   -O3    Optimize yet more.  -O3 turns on all optimizations
          specified   by   -O2   and   also   turns  on  the
          -finline-functions and -frename-registers options.

我还确认了代码,以确保两个选项是-O3包含的唯一两个优化:

if (optimize >= 3){
    flag_inline_functions = 1;
    flag_rename_registers = 1;
}

对于这两个优化:

在某些情况下(主要使用C ++),

-finline-functions 非常有用,因为它允许我们使用-finline-limit定义内联函数的大小(默认为600)。当设置高内联限制时,编译器可能会报告错误,抱怨内存不足。

-frename-registers 尝试通过使用寄存器分配后遗留的寄存器来避免调度代码中的错误依赖。这种优化最有利于具有大量寄存器的处理器。

对于内联函数,尽管它可以减少函数调用的数量,但它可能会导致大的二进制文件,因此-finline-functions可能会引入严重的缓存惩罚并且甚至比-O2慢。我认为缓存处罚不仅取决于程序本身。

对于重命名寄存器,我认为它不会对像x86这样的cisc架构产生任何积极影响。

我的问题有2.5部分:

[Answerd] 1。我是否正确地声称程序是否可以使用-O3选项更快地运行取决于底层平台/架构?

编辑: 第一部分已被确认为真实。 David Hammen还声称,对于优化和浮点运算如何在具有扩展精度浮点寄存器(如Intel和AMD)的机器上进行交互,我们应该非常小心。

2. 我什么时候可以自信地使用-O3选项?我认为这两个优化尤其是重命名寄存器可能会导致与-O0 / O2不同的行为。我看到一些用-O3编译的程序在执行过程中崩溃了,它是确定性的吗?如果我在没有任何崩溃的情况下运行一次可执行文件,是否意味着使用-O3是安全的?

编辑:确定性与优化无关,它是一个多线程问题。但是,对于多线程程序,当我们运行一次可执行文件而没有错误时,使用-O3是不安全的。 David Hammen表明,浮点运算的O3优化可能违反严格的弱排序标准进行比较。 当我们想要使用-O3选项时,是否还需要注意其他问题?

[回答] 3。如果第一个问题的答案是“是”,那么当我更改目标平台或使用不同机器的分布式系统时,我可能需要在-O3和-O2之间进行更改。有没有一般的方法来决定我是否可以通过-O3获得性能提升?例如,更多寄存器,短内联函数等

编辑:第3部分已经被Louen回答为“各种平台使得这个问题的一般推理变得不可能”当通过-O3评估性能增益时,我们必须尝试两者并对我们的代码进行基准测试以查看哪个更快。

2 个答案:

答案 0 :(得分:6)

  
      
  1. 我看到一些程序在使用-O3进行编译时崩溃了,这是确定性的吗?
  2.   

如果程序是单线程的,程序使用的所有算法都是确定性的,如果运行到运行的输入是相同的,是的。如果这些条件中的任何一个不成立,答案是“不一定”。

如果不使用-O3进行编译,则同样适用。

  

如果我在没有任何崩溃的情况下运行一次可执行文件,是否意味着使用-O3是安全的?

当然不是。如果不使用-O3进行编译,同样适用。仅仅因为您的应用程序运行一次并不意味着它将在所有情况下成功运行。这是使测试成为一个难题的部分原因。


浮点运算可能会导致浮点寄存器具有比双精度更高精度的机器上的奇怪行为。例如,

void add (double a, double b, double & result) {
   double temp = a + b;
   result = temp;
   if (result != temp) {
      throw FunkyAdditionError (temp);
   }
}

编译使用此add函数未经优化的程序,您可能永远不会看到任何FunkyAdditionError异常。编译优化和某些输入将突然启动导致这些异常。问题是,通过优化,编译器将使temp成为寄存器,而作为引用的result将不会被编译到寄存器中。添加inline限定符,当使用-O3编译编译器时,这些异常可能会消失,因为现在result也可以是寄存器。关于浮点运算的优化可能是一个棘手的主题。

最后,让我们看看其中一个案例,当使用-O3,GCC: program doesn't work with compilation option -O3编译程序时,事情确实发生了变化。问题只发生在-O3上,因为编译器可能内联了distance函数,但在扩展精度浮点寄存器中保留了一个(但不是两个)结果。通过此优化,某些点p1p2可以导致p1<p2p2<p1评估为true。这违反了比较函数的严格弱排序标准。

关于优化和浮点运算如何在具有扩展精度浮点寄存器(例如,Intel和AMD)的机器上进行交互,您需要非常小心。

答案 1 :(得分:4)

1)和3)你是对的。某些程序可以从-O3启用的优化中受益,而某些程序则不会。例如,内联更多函数通常更好(因为它绕过函数调用机制开销),但有时它会使事情变慢(例如通过削弱缓存局部性)。那个和各种各样的平台使得这个问题的一般推理变得不可能。

所以简而言之,唯一有效的答案是:尝试两者并对代码进行基准测试,看看哪个更快。

2)假设您没有遇到任何编译器/优化器错误(它们很少见,但它们存在),那么可以合理地假设程序中的错误仅在-O3处显示,然后它可能一直存在,只有-O3选项才发现它。