0

Consider the code below.


#include <iostream>
#include <random>
#include <chrono>
#include <memory>
const int N = 1 << 28;
int main()
{
    const int seed = 0;
    std::mt19937 gen;
    std::uniform_real_distribution<double> dis;
    std::normal_distribution<double> normal;
    std::unique_ptr<bool[]> array = std::unique_ptr<bool[]>(new bool[N]);

    for (int i = 0; i < N; i++)
    {
        if (dis(gen) > 0.5)
            array[i] = true;
        else
            array[i] = false;
    }

    int sum = 0;
    std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < N; i++)
    {
        if (array[i])
            sum++;
    }
    auto t2 = std::chrono::high_resolution_clock::now();

    std::cout << std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() << " microsecond" << std::endl;
    std::cout << sum << std::endl;




         sum = 0;
     t1 = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < N; i++)
    {
            sum+=array[i];
    }
     t2 = std::chrono::high_resolution_clock::now();

    std::cout << std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() << " microsecond" << std::endl;
    std::cout << sum << std::endl;
}

If I comment lines with std::cout << sum << std::endl; then the execution times will shown as zeros ( or close enough) . I have checked it on different compilers, including icpc, icl (v19.1.2) and g++ ( v9.2) with O3 compilation flag.

Is this an example of out-of-order (dynamic) execution?

user3786219
  • 177
  • 1
  • 2
  • 11
  • 1
    If you comment out those lines, the optimizer will likely turn your program into doing nothing since that won't have any noticable side effects (except for the execution time). – Ted Lyngmo Nov 25 '20 at 22:12

1 Answers1

1

Without the lines

std::cout << sum << std::endl; 

The compiler will realize that removing this

for (int i = 0; i < N; i++)
{
    if (array[i])
        sum++;
}

has no observable effect (same is true for both loops that calcualte sum). Hence also this

for (int i = 0; i < N; i++)
    {
        if (dis(gen) > 0.5)
            array[i] = true;
        else
            array[i] = false;
    }

can be removed without observable effect.

This is an example of the so called as-if-rule. In a nutshell, as long as the observable behavior does not change the compiler can do anything. For more details, see What exactly is the "as-if" rule?

Measuring the runtime does not count as observable behavior of the program by the way.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • How do I measure the execution time(s) without worrying about the "as-if" rule? – user3786219 Nov 25 '20 at 22:25
  • @user3786219 thats extremely difficult to do reliably. Either you measure the real application, not just micro benchmarks. Then you are sure that the optimized code is what you want to measure. Or you use a tool, eg https://quick-bench.com/. I don't know it that much, but as you can see it has special features to inhibit unwanted optimizations (`benchmark::DoNotOptimize(created_string);`) – 463035818_is_not_an_ai Nov 25 '20 at 22:29
  • 1
    @user3786219 the simple answer is: always use the result, eg print it on the screen. But as mentioned it is difficult to make sure you measure what you actually want to measure. – 463035818_is_not_an_ai Nov 25 '20 at 22:32
  • 1
    @user3786219 https://quick-bench.com/ can be helpful. – Ted Lyngmo Nov 25 '20 at 22:52
  • @user3786219: You *always* have to worry about compiler optimizations when micro-benchmarking. That's kind of the whole point: to see exactly what the compiler did in a given context of surrounding code that uses some of the results, but doesn't care about the results of local temporary vars. And how well that asm runs on a given CPU. So yes of course you have to control which parts of the code are necessary outputs that the compiler has to produce. – Peter Cordes Dec 11 '20 at 19:13