Allowed compiler optimizations in C ++ 11 loops

It is a C ++ 11 compatible compiler that allows you to optimize / convert this code from:

bool x = true; // *not* an atomic type, but suppose bool can be read/written atomically /*...*/ { while (x); // spins until another thread changes the value of x } 

on everything that is equivalent to an infinite loop:

 { while (true); // infinite loop } 

The above transformation is certainly true from the point of view of a single-threaded program, but this is not a general case.

Also, was optimization allowed in pre-C ++ 11?

+8
c ++ language-lawyer
source share
2 answers

Absolutely.

Since x not marked as volatile and seems to be a local object with automatic storage time and internal communication, and the program does not change it, both programs are equivalent.

In both C ++ 03 and C ++ 11, this is done according to the as-if rule, since access to a non-volatile object is not considered a β€œside effect” of the program:

[C++11: 1.9/12]: Access to an object indicated by a mutable glvalue (3.10), modifying an object, calling a library I / O function, or calling a function that makes any of these operations all side effects that change state of the environment fulfillment. Evaluating an expression (or subexpression) generally includes both calculating the values ​​(including determining the identity of the object to evaluate the glvalue and fetching the value previously assigned to the object to evaluate the evaluation) and triggering side effects. When a call to a library I / O function is returned or access to a volatile object is calculated, the side effect is considered complete, even if some external actions are implied by a call (for example, I / O itself) or unstable access may not be completed.

C ++ 11 frees up space for a global object to change its value in one thread, and then a new value read in another:

[C++11: 1.10/3]: The value of an object that is visible to a stream T at a certain point is the initial value of the object, the value assigned to the object, T , or the value assigned to the object by another stream , in accordance with the rules below.

However, if you do this because your object is not atomic:

[C++11: 1.10/21]: A program execution contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and does not happen before the other. Any such data race results in undefined behavior.

And when undefined behavior is called, anything can happen.

Bootnote

[C++11: 1.10/25]: The implementation must ensure that the last value (in the order of modification) assigned by atomic or synchronization becomes visible to all other threads over a finite period of time.

Again, note that to obtain this guarantee, the object must be atomic (for example, std::atomic<bool> ).

+13
source share

The compiler allows you to do something with these two loops. Including program termination. Since infinite loops have undefined behavior, if they do not perform an operation similar to synchronization (do something that requires synchronization with another thread or I / O), according to the C ++ memory model :

Note that this means that a program with infinite recursion or an infinite loop (regardless of whether it is implemented as a for-statement or by iterating through goto or otherwise) has undefined behavior.

+1
source share

All Articles