可以重新排序的语句

时间:2016-06-27 22:16:19

标签: c++

假设我们有以下三个代码段:

  1. // both a and b are non-volatile ints
    a = 123;
    b = 456;
    
  2. // both a and b are non-volatile ints
    a = rand();
    b = rand();
    
  3. cout << "foo" << endl;
    cout << "bar" << endl;
    
  4. 根据我的理解,(1)中的陈述可以由编制者重新排序,而(2)(3)中的陈述则不能,因为这会改变程序的可观察行为。

    编译器如何知道什么时候不能重新排序的东西,当它们不那么“明显”依赖时(像++a; b = a * 2;这样的陈述显然是依赖的),如(2)所示( 3)?例如,可能某些事情(如非constexpr函数调用会阻止重新排序......?

3 个答案:

答案 0 :(得分:9)

The only standard rule that governs reordering (and optimization in general) is the "as if" rule, meaning that the compiler can do anything that it pleases if it determines that the end result is the same, and the standard won't get in the way. However, when we get to that level, the compiler probably no longer operates on the C++ source level: it's probably dealing with an intermediate form, and thinking in statements might not actually best reflect what is going on.

For instance, in your second example:

// both a and b are non-volatile ints
a = rand();
b = rand();

The compiler could call rand twice and store the result of the second rand call to b before storing the result of the first rand call to a. In other words, the assignments have been reordered but the calls haven't. This is possible because the compiler optimizes a representation of your program that is more granular than C++.

Various compilers use various tricks to determine whether two intermediate representation instructions can be reordered, but most often, function calls are insurmountable barriers that cannot be reordered. Especially, calls to external libraries (like rand) can't even be analyzed and are certain to not be reordered since the compiler will prefer a conservative but known-correct approach.

In fact, function calls can only be reordered if the compiler determines that they cannot interfere with one another. Compilers attempt to solve this problem with alias analysis. The idea is that beyond value dependence (for instance, in a * b + c, the + operation depends on the result of a * b and thus a * b must happen first), ordering (almost) only matters when you write somewhere and read back from there later. This means that if you can correctly identify every memory operation and their impact, you can determine if two memory operations can be reordered, or even eliminated altogether. For these purposes, a call is considered a big memory operation that encompasses all the smaller loads and stores that it does.

Unfortunately, the general case of alias analysis is known to be uncomputable. While compilers get smarter and smarter, you'll probably never have a compiler that is systematically able to make the best decision on call reordering even if you had all of the source code that you link against.

Some compilers have attributes specific to themselves that govern whether they should consider the function safe for reordering, regardless of their own analysis. For instance, gcc will happily reorder, cache or even eliminate function calls with the [[gnu::pure]] attribute if it thinks that it will lead to a performance increase.

答案 1 :(得分:1)

为了可靠地满足标准的要求,编译器只有在“明显”不依赖时才会重新排序语句。

如果不确定,则无法重新订购。所以没有问题。

答案 2 :(得分:0)

指令重新排序&#34;仅&#34;在单线程程序行为没有改变的情况下。