C++ Utilities
Loading...
Searching...
No Matches
The Stopwatch Class

The <utilities/stopwatch.h> header defines utilities::stopwatch, a simple stopwatch class for measuring execution time.

You might use it like this:

do_work();
std::cout << sw << '\n';
See the Stopwatch page for all the details.
Definition stopwatch.h:16

The output will be the time taken for the do_work() call. The units are seconds.

Declaration

The header-only class is templatised over a specific clock choice, likely one of the clocks from std::chrono.

template<typename Clock> class utilities::stopwatch;

Notes

The default clock is std::chrono::high_resolution_clock.

Discussions on sites like Stack Overflow instead favour std::chrono::steady_clock. That clock is guaranteed to be monotonic and unaffected by changes to the system time, making it suitable for long-running processes.

However, in practice, we primarily examine much shorter code blocks and value accuracy over "steadiness." We also note that, in any case, the two clocks are often identical!

If the clock choice matters, you can use one of the following type aliases:

stopwatch< std::chrono::system_clock > system_stopwatch
A stopwatch that is uses the system clock.
Definition stopwatch.h:104
stopwatch< std::chrono::steady_clock > steady_stopwatch
A stopwatch that is guaranteed to be monotonic.
Definition stopwatch.h:101
stopwatch< std::chrono::high_resolution_clock > precise_stopwatch
Theoretically the most precise stopwatch – it might get put off by system reboots etc.
Definition stopwatch.h:98

We always store elapsed times as a double number of seconds — this is also contrary to the advice to use std::chrono::duration.

However, the primary goal for utilities::stopwatch is ease of use. Sticking to seconds as the standard unit makes everything consistent. Besides, a double number of seconds gives you 15 or 16 places of accuracy, which is enough for any conceivable application.

Remarks
Use the utilities::stopwatch class for cheap-and-cheerful performance measurement. It is not a replacement for the many more complete, but more complex, code profiling tools.

Construction

utilities::stopwatch(const std::string& name = "");
std::string name() const
Provides read-only access to the stopwatch's name.
Definition stopwatch.h:25

Creates a stopwatch and sets its zero time to the current time. You can give it a name, which is helpful if multiple stopwatches run in one executable.

Timing Methods

The stopwatch class is kept deliberately simple.

At its core, it measures the elapsed time in seconds from a zero time set on creation or by calling the stopwatch's reset() method. It also supports the idea of clicking a stopwatch to record a split time — the time, in seconds, from the zero point to the click event. Finally, it records a _lap time, which is the time between the last two stopwatch clicks. However, it has no memory beyond that.

constexpr void reset(); // <1>
constexpr double elapsed() const; // <2>
constexpr double click(); // <3>
constexpr double split() const; // <4>
constexpr double lap() const; // <5>
constexpr void reset()
Set/reset the stopwatch's 'zero' point & clear any measured splits.
Definition stopwatch.h:31
constexpr double elapsed() const
Get the time that has passed from the zero point to now. Units are seconds.
Definition stopwatch.h:39
constexpr double lap() const
Returns the last 'lap' time in seconds (i.e. the time between prior 2 splits).
Definition stopwatch.h:60
constexpr double click()
Clicks the stopwatch to create a new 'split' and returns the elapsed time in seconds.
Definition stopwatch.h:48
constexpr double split() const
Returns the split as the time in seconds that elapsed from the zero point to the last click.
Definition stopwatch.h:57
  1. Clears out any recorded split time and resets the zero time to now.
  2. Returns the number of seconds from the zero_time to now.
  3. Creates a split by recording the elapsed time in seconds from the zero time to the click() call and returns that time.
  4. Returns the last recorded split time in seconds.
  5. Returns the time in seconds between the last two splits — i.e., between the previous two click events.

Other Methods

You read and set the name of the stopwatch:

const std::string& name() const;
std::string& name();

Output Functions

We have the usual stream output operator and also a specialisation for [std::formatter]:

template<typename Clock>
std::ostream &
operator<<(std::ostream&, const utilities::stopwatch<Clock>&);
template<typename Clock>
struct std::formatter<utilities::stopwatch<Clock>>;
The namespace for the utilities library.
Definition formatter.h:14
std::ostream & operator<<(std::ostream &os, const stopwatch< Clock > &rhs)
The usual output operator.
Definition stopwatch.h:84
Note
These functions output only the stopwatch's elapsed time. The output will look like "3.2s" without a name. The output will look like "name: 3.2s" with a name.

We also have a little utility to convert a std::chrono::duration to a double number of seconds.

template<class Rep, class Period>
constexpr double
to_seconds(const std::chrono::duration<Rep, Period>);

Example

How efficient is it to put a thread to sleep?

#include <format>
#include <thread>
int main()
{
using namespace std::literals;
for (auto sleep_duration = 0ms; sleep_duration <= 2s; sleep_duration += 200ms) {
sw.click(); // <1>
std::this_thread::sleep_for(sleep_duration); // <2>
sw.click(); // <3>
double sleep_ms = 1000 * utilities::to_seconds(sleep_duration); // <4>
double actual_ms = 1000 * sw.lap(); // <5>
double diff = actual_ms - sleep_ms;
double percent = sleep_ms != 0 ? 100 * diff / sleep_ms : 0;
std::println("Requested sleep for {:8.2f}ms, measured wait was {:8.2f}ms => overhead {:.2f}ms ({:.2f}%)",
sleep_ms, actual_ms, diff, percent);
}
std::println("Total elapsed time: {}", sw);
return 0;
}
constexpr double to_seconds(const std::chrono::duration< Rep, Period > &d)
A convenience function that converts a std::chrono::duration to a double number of seconds.
Definition stopwatch.h:92
A stopwatch class to measure execution times. See the Stopwatch page for all the details.
  1. Create a split.
  2. Sleep for a set number of milliseconds.
  3. Create a second split and, hence, a lap.
  4. Convert the sleep duration to a double number of seconds.
  5. Get the lap time for that last call to sleep_for(...).

The program outputs something along the lines:

Requested sleep for 0.00ms, measured wait was 0.00ms => overhead 0.00ms (0.00%)
Requested sleep for 200.00ms, measured wait was 202.96ms => overhead 2.96ms (1.48%)
Requested sleep for 400.00ms, measured wait was 401.59ms => overhead 1.59ms (0.40%)
Requested sleep for 600.00ms, measured wait was 604.82ms => overhead 4.82ms (0.80%)
Requested sleep for 800.00ms, measured wait was 804.06ms => overhead 4.06ms (0.51%)
Requested sleep for 1000.00ms, measured wait was 1001.97ms => overhead 1.97ms (0.20%)
Requested sleep for 1200.00ms, measured wait was 1202.99ms => overhead 2.99ms (0.25%)
Requested sleep for 1400.00ms, measured wait was 1405.10ms => overhead 5.10ms (0.36%)
Requested sleep for 1600.00ms, measured wait was 1603.67ms => overhead 3.67ms (0.23%)
Requested sleep for 1800.00ms, measured wait was 1803.70ms => overhead 3.70ms (0.21%)
Requested sleep for 2000.00ms, measured wait was 2004.82ms => overhead 4.82ms (0.24%)
Total elapsed time: 11.036255763s