Home > Net >  Are 'volatile' and 'side effect' related?
Are 'volatile' and 'side effect' related?

Time:07-24

Just a beginner question.

In the code from cppreference.com, there is a comment saying, "make sure it's a side effect" on the line using std::accumulate.

  1. What is the author's intention in saying this? Is it related to the volatile int sink? (Maybe it means something like "make sure the sink is not optimized by the compiler"?)

  2. Why volatile for sink? I think almost every compiler might know the std::accumulate will change the value of sink.

I thought I knew what volatile and "side-effect" meant, but maybe I'm missing some point!

#include <iostream>
#include <iomanip>
#include <vector>
#include <numeric>
#include <chrono>
 
volatile int sink;
int main()
{
    std::cout << std::fixed << std::setprecision(9) << std::left;
    for (auto size = 1ull; size < 1000'000'000ull; size *= 100) {
        // record start time
        auto start = std::chrono::system_clock::now();
        // do some work
        std::vector<int> v(size, 42);

        // This is the line I mentioned.
        sink = std::accumulate(v.begin(), v.end(), 0u); // make sure it's a side effect

        // record end time
        auto end = std::chrono::system_clock::now();
        std::chrono::duration<double> diff = end - start;
        std::cout << "Time to fill and iterate a vector of " << std::setw(9)
                  << size << " ints : " << diff.count() << " s\n";
    }
}

CodePudding user response:

Not necessarily.

C has the 'as if' rule. The compiler must generate code that works 'as if' the source code was executed, but it doesn't have to do everything that the source code does, in exactly the same order.

Now look at the sink variable. After the line you mention it's never used again. So it's value has no visible effect on the program. So why should the compiler bother calculating it's value? Because of the as if rule it is perfectly legal for the compiler not to do so.

But there are a few exceptions to the as if rule, and one of them is volatile variables. Reads and writes to volatile variables must occur in exactly the way that the source code says. That's why volatile is important in this code. It forces the compiler to execute the std::accumulate call even though it has no visible effect on the program.

Further reading https://en.cppreference.com/w/cpp/language/as_if

CodePudding user response:

The issue in the code sample given is that the value of sink is never used after it has been calculated. Thus, a compiler would be free to "optimize out" the call to std::accumulate, as doing so would have no observable effect.

Adding the volatile qualifier to sink prevents such an undesired optimization. From another cppreference page (bold emphasis mine):

volatile object - An object whose type is volatile-qualified, or a subobject of a volatile object, or a mutable subobject of a const-volatile object. Every access (read or write operation, member function call, etc.) made through a glvalue expression of volatile-qualified type is treated as a visible side-effect for the purposes of optimization (that is, within a single thread of execution, volatile accesses cannot be optimized out or reordered with another visible side effect that is sequenced-before or sequenced-after the volatile access. …)

CodePudding user response:

The example code referenced is intended to show timing functions. The C standard allows the compiler to look at some code, and remove it, if it believes the result is not used.

The example uses volatile and the side effect to be sure that the timing is not optimized away by the compiler.

  • Related