Step 4 of 7
Measuring time
<chrono> has several clocks, and picking the wrong one is a classic bug:
| Clock | Use it for |
|---|---|
std::chrono::steady_clock |
measuring elapsed time: it never jumps backwards |
std::chrono::system_clock |
wall-clock time ("now" as a date); can jump when the OS adjusts the time |
A clock's now() returns a time point. Subtracting two time points gives a duration, which you can cast to whatever unit you want.
#include <chrono>
#include <iostream>
long slow_sum(int n) {
long s = 0;
for (int i = 0; i < n; i++) s += i % 7;
return s;
}
int main() {
auto start = std::chrono::steady_clock::now();
volatile long result = slow_sum(2'000'000);
auto elapsed = std::chrono::steady_clock::now() - start;
auto us = std::chrono::duration_cast<std::chrono::microseconds>(elapsed);
std::cout << "result " << result << "\n";
std::cout << "never negative: " << (us.count() >= 0) << "\n"; // the real number varies per run
}
result 5999995
never negative: 1
Why steady_clock
The system clock follows the wall clock, which gets corrected: network time sync nudges it, daylight saving shifts it, a user changes it. If that happens during a measurement, a system_clock interval can be wrong or even negative. steady_clock only ever moves forward at a constant rate, so it's the right choice for intervals, timeouts and benchmarks.
The volatile stops the optimizer from removing the work whose result is never used otherwise. Benchmark libraries have helpers for this.
Your task: timing any callable
Accept the callable as a forwarding reference F&& f (from the variadic templates module), take now() before and after calling f(), and duration_cast the difference to std::chrono::microseconds.
Your turn: write template <typename F> std::chrono::microseconds time_it(F&& f) that calls f() once and returns how long it took.
Previous: chrono durations Next: Strict number parsing with from_chars