Stopwatch Class
Introduction
The <utilities/stopwatch.h>
header defines utilities::stopwatch
, a simple stopwatch class to measure execution times.
You might use it like this:
::stopwatch sw;
utilities();
do_workstd::cout << sw << '\n';
The output will be the time taken in seconds for the do_work()
call to run.
Declaration
template<typename Clock = std::chrono::high_resolution_clock>
class utilities::stopwatch;
The header-only class is templatized over the specific clock choice, likely one of the clocks from std::chrono
.
Our 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 changing the system time, so it is 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:
::precise_stopwatch = utilities::stopwatch<std::chrono::high_resolution_clock>;
utilities::steady_stopwatch = utilities::stopwatch<std::chrono::steady_clock>;
utilities::system_stopwatch = utilities::stopwatch<std::chrono::system_clock>; utilities
We always store elapsed times as a double
number of seconds — this is also contrary to advice that advocates the use of std::chrono::duration
.
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.
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
::stopwatch(const std::string& name = ""); utilities
Creates a stopwatch and sets its zero time to now. 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 further back than that.
1constexpr void reset();
2constexpr double elapsed() const;
3constexpr double click();
4constexpr double split() const;
5constexpr double lap() const;
- 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
1const std::string& name() const;
std::string& name();
- 1
- Read-only and read-write access to the stopwatch name field.
Output Functions
template<typename Clock>
std::ostream &
1operator<<(std::ostream&, const utilities::stopwatch<Clock>&);
template<typename Clock>
2struct std::formatter<utilities::stopwatch<Clock>>;
- 1
- The usual output operator for a stopwatch.
- 2
-
Adds stopwatch support to
std::format
.
These functions output the stopwatch’s elapsed time only. The output will look like “3.2s” without a name. The output will look like “name: 3.2s” with a name. |
Other Functions
template<class Rep, class Period>
constexpr double
1(const std::chrono::duration<Rep, Period>); to_seconds
- 1
-
This converts a
std::chrono::duration
to adouble
number of seconds.
Example: How efficient is it to put a thread to sleep?
#include <utilities/stopwatch.h>
#include <format>
#include <thread>
int main()
{
using namespace std::literals;
::stopwatch sw;
utilities
for (auto sleep_duration = 0ms; sleep_duration <= 2s; sleep_duration += 200ms) {
1.click();
sw2std::this_thread::sleep_for(sleep_duration);
3.click();
sw
4double sleep_ms = 1000 * utilities::to_seconds(sleep_duration);
5double actual_ms = 1000 * sw.lap();
double diff = actual_ms - sleep_ms;
double percent = sleep_ms != 0 ? 100 * diff / sleep_ms : 0;
std::cout << std::format("Requested sleep for {:8.2f}ms, measured wait was {:8.2f}ms => overhead {:.2f}ms ({:.2f}%)\n",
, actual_ms, diff, percent);
sleep_ms}
std::cout << "Total elapsed time: " << sw << '\n';
return 0;
}
- 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(...)
.
Output
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