C/C++ Arena

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 task

Write template <typename F> std::chrono::microseconds time_it(F&& f) that calls f() once and returns how long it took.

Challenge 2

Write template <typename F> double average_ms(F&& f, int runs) that calls f() runs times and returns the average time per call in milliseconds, as a double. Use std::chrono::steady_clock and std::chrono::duration<double, std::milli>. With runs <= 0, call nothing and return 0.

Challenge 3

Write class Stopwatch that adds up running time across several start and stop pairs, like a chess clock. start() starts it (no effect if it's already running), stop() adds the time since the start to the total (no effect if it's stopped), running() says whether it's running, and elapsed() returns the total so far as std::chrono::nanoseconds, including the current run if it's running.

Previous: chrono durations Next: Strict number parsing with from_chars