Featured image of post C++ Performance Timer

C++ Performance Timer

A simple class for measuring time elapsed

The Chrono Library

The <chrono> library contains functions for handling time related things.

The code in these examples uses the C++20 language standard.

Clock Types

Use the system_clock when you want to get the current calendar date and time.

Use the steady_clock when you want to measure time elapsed. This is likely suitable for many benchmarking needs.

Use the high_resolution_clock for benchmarking and profiling. This may be the same as the previous two, so it may be better to implement your own high resolution clock using OS features. This guide will explain how to do this for Windows and Linux.

Getting The Current Time

Using chrono we can check the current date and time.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <iostream>
#include <chrono>

using namespace std;
using namespace std::chrono;

int main()
{
    time_point<system_clock> t = system_clock::now();

    cout << "Current Time: " << t << endl;
}

This program will output something like this

1
Current Time: 2021-06-18 02:37:59.7503284

Measuring Time

It is common to want to measure how much time something takes to run, like for example when benchmarking your code for performance.

For this purpose, we can use the steady_clock to measure time elapsed like a stopwatch.

The Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <chrono>
#include <thread>
#include <ratio>

using namespace std;
using namespace std::chrono;
using namespace std::chrono_literals;
using std::this_thread::sleep_for;

int main()
{
    time_point<steady_clock> startTime = steady_clock::now();

    sleep_for(150ms);

    time_point<steady_clock> endTime = steady_clock::now();

    duration<double> s = endTime - startTime;
    duration<double,milli> ms = endTime - startTime;
    auto us = duration_cast<microseconds>(endTime - startTime);
    auto ns = endTime - startTime;

    cout << "Total seconds: " << s.count() << endl;
    cout << "Total milliseconds: " << ms.count() << endl;
    cout << "Total microseconds: " << us.count() << endl;
    cout << "Total nanoseconds: " << ns.count() << endl;
}

This program will output something like the following

1
2
3
4
Total seconds: 0.162912
Total milliseconds: 162.912
Total microseconds: 162912
Total nanoseconds: 162912300

Line By Line

We will first get the current time and then do some lengthy operation to see how long it takes.

1
    time_point<steady_clock> startTime = steady_clock::now();

We will just simulate something taking a long time with the sleep function.

The sleep function will try to sleep for some amount of time, but the actual amount of time is likely to vary due to system noise.

1
    sleep_for(150ms);

After the operation we will again obtain the current time.

1
    time_point<steady_clock> endTime = steady_clock::now();

We can then find the total time elapsed by subtracting the start and end times.

For example, if the start time is 9:30AM and the end time is 9:35AM then five minutes have gone by.

This line will get the elapsed time in seconds as a floating point number.

1
    duration<double> s = endTime - startTime;

We can get a different resolution by assigning to a duration with a different ratio.

This line will get the time in milliseconds as a float.

1
    duration<double,milli> ms = endTime - startTime;

We can get a different resolution with duration_cast.

This line will get the time in microseconds as an integer.

1
    auto us = duration_cast<microseconds>(endTime - startTime);

The default resolution will be integer nanoseconds.

1
    auto ns = endTime - startTime;

The .count() function returns the int or float value of the duration.

A Timer Class

Here is an example time measurement class you can use.

This class provides a slightly nicer more convenience syntax for performance benchmarking your code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <chrono>
#include <thread>
#include <ratio>

using namespace std;
using namespace std::chrono;
using namespace std::chrono_literals;
using std::this_thread::sleep_for;

class Stopwatch {
public:
    using Clock = steady_clock;
    using Time = time_point<Clock>;
    using Duration = duration<double,milli>;

private:
    Time startTime;
    Time endTime;

public:
    Stopwatch() {
        start();
    }

    void start() {
        startTime = Clock::now();
        endTime = startTime;
    }

    Duration stop() {
        endTime = Clock::now();
        return get();
    }

    Duration get() {
        return endTime - startTime;
    }
};

int main()
{
    Stopwatch s;
    sleep_for(150ms);

    auto totalTime = s.stop();
    auto ns = duration_cast<nanoseconds>(totalTime);

    cout << "Total milliseconds: " << totalTime.count() << endl;
    cout << "Total nanoseconds: " << ns.count() << endl;
}

Cover Photo

Credit: Max Shein