我看到很多人提示 -O3
选项:
-O3
我查看 GCC 的手册:
而且我还确认了代码以确保两个选项是
-O3
on 中唯一包含的两个优化:if (optimize >= 3){
flag_inline_functions = 1;
flag_rename_registers = 1;
}
对于这两个优化:-finline-functions
在某些情况下(主要是 C++)很有用,因为它允许我们使用 -finline-limit 定义内联函数的大小(默认为 600)。设置高内联限制时,编译器可能会报告错误,提示内存不足。 -frename-registers
试图通过使用寄存器分配后剩余的寄存器来避免调度代码中的错误依赖。这种优化最有利于具有大量寄存器的处理器。 对于内联函数,虽然可以减少函数调用次数,但可能会导致二进制文件过大,因此
-finline-functions
可能会引入严重的缓存惩罚,甚至比-O2还要慢。我认为缓存惩罚不仅取决于程序本身。对于重命名寄存器,我认为它不会对像 x86 这样的 cisc 架构产生任何积极影响。
我的问题有 2.5 个部分:
编辑:
第一部分已被确认为真实。 David Hammen 还声称,我们应该非常小心优化和浮点运算如何在具有扩展精度浮点寄存器(如 Intel 和 AMD)的机器上进行交互。
-O3
选项? 我想这两个优化尤其是重命名寄存器可能会导致与 -O0/O2 不同的行为。我看到一些用 -O3
编译的程序在执行过程中崩溃了,这是确定性的吗?如果我运行一次可执行文件而没有任何崩溃,是否意味着使用 -O3
是安全的?编辑:确定性与优化无关,它是一个多线程问题。但是,对于多线程程序,当我们运行一次可执行文件没有错误时,使用
-O3
是不安全的。 David Hammen 表明,-O3
对浮点运算的优化可能会违反用于比较的严格弱排序标准。 当我们想使用 -O3
选项时,还有其他需要注意的问题吗? -O3
和 -O2
之间进行更改。是否有任何通用方法可以决定我是否可以使用 -O3
获得性能改进?例如,更多的寄存器,短内联函数等。 [已回答]编辑:第三部分已由 Louen 回答为“平台的多样性使得对这个问题的一般推理变得不可能”当通过
-O3
评估性能增益时,我们必须同时尝试并测试我们的代码以查看哪个更快。最佳答案
如果程序是单线程的,则程序使用的所有算法都是确定性的,并且如果从运行到运行的输入相同,则是。如果这些条件中的任何一个不正确,答案是“不一定”。
如果不使用 -O3 进行编译,则同样适用。
当然不是。同样,如果您不使用 -O3 进行编译,则同样适用。仅仅因为您的应用程序运行一次并不意味着它在所有情况下都能成功运行。这是使测试成为难题的部分原因。
在浮点寄存器的精度比 double 高的机器上,浮点运算会导致奇怪的行为。例如,
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
函数,但将结果中的一个(但不是两个)保存在扩展精度浮点寄存器中。通过这种优化,某些点 p1
和 p2
会导致 p1<p2
和 p2<p1
计算为 true
。这违反了比较函数的严格弱排序标准。您需要非常小心优化和浮点运算如何在具有扩展精度浮点寄存器的机器(例如,Intel 和 AMD)上进行交互。
关于c++ - 我什么时候可以自信地用 -O3 编译程序?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14850593/