Search code examples
c++performanceoptimizationtime-measurementas-if

Timing vs the "as-if" rule


There is a great question about the "as-if" rule in general, but I wonder if there are any exceptions when it comes to measuring time.

Consider this (taken from here slightly modified):

using std::chrono;
auto begin = steady_clock::now();

auto result = some_lengthy_calculation(some_params);

auto end = std::chrono::steady_clock::now();

std::cout << "Time diff = " << duration_cast<microseconds>(end - begin).count() <<std::endl;
std::cout << "Result = " << result;

The compiler is allowed to apply any optimization that results in the same result. The point here is that the "as-if" rule does not apply to the measured time directly. Of course the measured time is not something that should be constant when optimizations are applied.

So my question is: How is it possible that I can reliably measure time with the above code when, according to the "as-if" rule, the compiler is allowed to rearrange it to one of the following?

auto temp = some_lengthy_calculation(some_params); // clever "optimization", precompute some stuff

auto begin = steady_clock::now();
auto result = temp;               // yay, I can use it here to pretend to be faster
auto end = steady_clock::now();

std::cout << "Time diff = " << duration_cast<microseconds>(end - begin).count() <<std::endl;
std::cout << "Result = " << result;

or even "more optimized":

std::cout << "Time diff = " << 42 <<std::endl;
std::cout << "Result = " << some_lengthy_calculation(some_params);

I assume no sane compiler would do that, but what exactly prevents a compiler from doing such "optimization"?

TL;DR...

  • One can observe the runtime difference between optimized and non-optimized code
  • If compiler optimizations are allowed to effect measured times, what prevents the compiler from not creating any code for the timing at all?

Solution

  • For the As-If rule to apply, the compiler must prove that the proposed change has no impact on the observable behavior. You are correct that the passage of time is not an observable behavior. However, in the case of reordering functions, it must prove that the order the functions are called in doesn't impact the observable behavior.

    Using timing features will invariably involve some mechanism for measuring time which the compiler will not be able to prove is safe to reorder. For example, it might involve a call to an opaque system API function or driver function that it can't inspect. If we take the most transparent example, a monotonic software clock that simply advances by 1 unit of time every time it's state is taken, there's no way to prove that the call order doesn't matter because it does matter.