不同的优化级别可以导致function上不同的代码?

我很好奇编译器在优化时的自由度。 让我们把这个问题限制在GCC和C / C ++(任何版本,任何标准的味道):

是否有可能编写的行为有所不同取决于编译的优化级别?

我想到的例子是在C ++中的各种构造函数中打印不同的文本位,并根据是否复制副本(虽然我还没有能够做出这样的工作)而有所不同。

计数时钟周期是不允许的。 如果你有一个非GCC编译器的例子,我也会好奇的,但是我不能检查它。 在C中的例子的奖励积分:-)

编辑:示例代码应符合标准,从一开始就不包含未定义的行为。

编辑2:已经有一些很好的答案! 让我来看看这个代码:代码必须是一个结构良好的程序,并且是符合标准的,它必须编译来纠正每个优化级别的确定性程序。 (不包括forms不规则的multithreading代码中的种族条件)。另外,我赞赏浮点舍入可能会受到影响,但我们打折。

我只打了800的声望,所以我觉得我要把第一个完整的例子中的50个声望作为赏金,以符合这些条件的精神; 25如果涉及滥用严格的走样。 (有人告诉我如何把赏金送给别人。)

适用的C ++标准的部分是§1.9“程序执行”。 它的部分内容是:

要求遵循的实现模仿(仅)抽象机器的可观察行为,如下所述。 …

执行格式良好的程序的一致性实现应产生与具有相同程序和相同input的抽象机器的相应实例的可能执行序列相同的可观察行为。 …

抽象机器的可观察行为是对易失性数据的读写顺序,以及对库I / O函数的调用。 …

所以,是的,代码在不同的优化级别上可能会有不同的performance ,但是(假设所有级别都生成一致的编译器),但是它们的行为不可能完全不同

编辑:允许我纠正我的结论:是的,只要每个行为与标准抽象机器的行为之一完全相同,代码在不同的优化级别上可能会有不同的performance。

是否有可能编写的行为有所不同取决于编译的优化级别?

只有当你触发编译器的错误。

编辑

这个例子在gcc 4.5.2上有不同的performance:

 void foo(int i) { foo(i+1); } main() { foo(0); } 

-O0编译创build程序崩溃与分段错误。
-O2编译创build一个进入无限循环的程序。

浮点计算是差异的成熟来源。 根据单个操作的sorting方式,您可以获得更多/更less的舍入错误。

less于安全的multithreading代码也可以有不同的结果,取决于如何优化内存访问,但这本质上是一个代码中的错误。

正如你所提到的,复制构造函数的副作用可以在优化级别改变时消失。

好吧,我为了这个赏金的公然游戏,提供了一个具体的例子。 我将把其他人的答复和我的意见放在一起。

针对不同优化级别的不同行为,“优化级别A”代表gcc -O0 (我使用的是4.3.4版本,但是没什么关系,我觉得最近的版本都会显示差异我在之后),“优化级别B”将表示gcc -O0 -fno-elide-constructors

代码很简单:

 #include <iostream> struct Foo { ~Foo() { std::cout << "~Foo\n"; } }; int main() { Foo f = Foo(); } 

优化级A的输出:

 ~Foo 

优化级别B的输出:

 ~Foo ~Foo 

代码是完全合法的,但是由于复制构造函数elision,输出是依赖于实现的,特别是它对gcc的优化标志非常敏感,从而禁用了copy ctor elision。

请注意,一般来说,“优化”是指编译器转换,它可以改变未定义,未指定或实现定义的行为,但不改变标准定义的行为。 因此任何满足您的标准的例子都是一个程序,其输出是未指定的或实现定义的。 在这种情况下,标准没有规定复制代码是否被删除,我只是碰巧很幸运GCC可以在任何时候都可靠地使用它们,但是可以select禁用它。

对于C来说,几乎所有的操作都是在抽象机器中严格定义的,只有当可观察结果与抽象机器完全相同时才允许进行优化。 想到这个规则的例外:

  • 未定义的行为不必在不同的编译器运行或者执行错误的代码之间保持一致
  • 浮点运算可能会导致不同的舍入
  • 函数调用的参数可以按任意顺序进行评估
  • 具有volatile限定types的expression式可能会或可能不会被评估仅仅是因为它们的副作用
  • 相同的const限定的复合文字可能会或可能不会被合并到一个静态内存位置

任何未定义的行为根据标准可以改变其行为取决于优化级别(或月相,就此而言)。

如果有两个指向同一块内存的指针, -fstrict-aliasing选项可以很容易地导致行为的改变。 这应该是无效的,但实际上是相当普遍的。

由于拷贝构造函数调用可以被优化,即使它们有副作用,具有副作用的拷贝构造函数也会导致未优化和优化的代码行为不同。

这个C程序调用未定义的行为,但在不同的优化级别中显示不同的结果:

 #include <stdio.h> /* $ for i in 0 1 2 3 4 do echo -n "$i: " && gcc -O$i xc && ./a.out done 0: 5 1: 5 2: 5 3: -1 4: -1 */ void f(int a) { int b; printf("%d\n", (int)(&a-&b)); } int main() { f(0); return 0; } 

在我的操作系统课程今天有一个有趣的例子。 我们分析了一些可能因优化而受损的软件互斥,因为编译器不知道并行执行。

编译器可以重新sorting不依赖于相关数据的语句。 正如我已经在并行代码中声明的,这个依赖对于编译器来说是隐藏的,所以它可能会被破坏。 我给出的例子会导致debugging的一些困难,因为线程安全性被破坏,并且由于OS调度问题和并发访问错误,你的代码行为不可预知。