How to Measure C++ Time Intervals

- select the date in page properties and the contributor at the end of the page -

To measure execution time in C++ using classes from the standard library, follow these three steps:

  1. Call high_resolution_clock::now at the start and finish points of the portion of code to be measured.

  2. Create an instance of the duration class with the difference between the start and finish time points recorded from Step 1.

  3. Invoke the duration::count method to get the elapsed time (in seconds).

As a C++ developer, it can be incredibly helpful knowing how to measure execution time in C++ for certain portions of code. That’s because understanding execution time intervals can be used for analyzing and optimizing important execution paths, or contrasting and comparing different options for algorithms and data structures. 

To get a better idea of how this all works, we will start by covering a few useful classes (available since C++11) for dealing with C++ time measurements. Then, we’ll look at how to implement measures using direct calls to Windows APIs.

C++ Time Classes

One option for measuring execution times (for a portion of C++ code) is to use some classes that are available as part of the C++11’s standard library. 

The high_resolution_clock class, defined in the standard <chrono> header, can come in handy here. As its name suggests, this class represents a clock with the highest precision (or equivalently, the smallest tick period) available on a given platform. The high_resolution_clock exposes the “now” method, which returns a value corresponding to the call’s point in time. 

You’ll want to invoke this method twice: 

  1. At the beginning portion of the code

  2. At the finish of that portion

Using this method, we record the start and the finish time of the execution.

#include <chrono>  // for high_resolution_clock
...


// Record start time
auto start = std::chrono::high_resolution_clock::now();

// Portion of code to be timed
...

// Record end time
auto finish = std::chrono::high_resolution_clock::now();

As we can note from the above code sample, the high_resolution_clock class is defined under the std::chrono namespace, and the now method is static. This means that you don’t need to create a new instance of the high_resolution_clock class. 

You’ll also notice that the return type of the now method is lengthy: std::chrono::time_point<std::chrono::high_resolution_clock>. 

Thankfully, you can use the C++11’s auto keyword to help reduce that clutter and increase code readability.

Once you’ve recorded the start and finish time of a given portion of code, you can use another class (the name of this class is duration and it represents a time interval) of the same std::chrono namespace to get the elapsed time. 

You can construct an instance of this class using the start and finish time previously recorded:

std::chrono::duration<double> elapsed = finish - start;

Once that’s in, you can invoke the count method of the duration class to get the elapsed time measured in seconds:

std::cout << "Elapsed time: " << elapsed.count() << " s\n";

How to Calculate Time in C++ with Windows High-Resolution Time Stamps

To start calculating time in C++ with Windows high-resolution time stamps in native user-mode C++ code, there are a couple of Windows APIs you’ll want to note. 

The first is QueryPerformanceCounter. This function retrieves the current value of the performance counter. This value is basically a high-resolution time stamp that can be used to measure C++ time intervals, similar to what we previously saw with the high_resolution_clock::now method.

Again, the idea is to call QueryPerformanceCounter twice:

  1. At the start of the portion code

  2. At the finish of the portion code

After the start and finish times are recorded, the time interval measurement can be done with some simple math. But before moving forward, note the usage of the QueryPerformanceCounter API. 

The prototype of this function is:

BOOL WINAPI QueryPerformanceCounter(
  _Out_ LARGE_INTEGER *lpPerformanceCount
);

The _Out_ annotation is a SAL annotation commonly used in Windows headers. It means that the IpPerformanceCounter parameter is an output parameter. 

In a nutshell: If the function succeeded, the current value of the performance counter is stored in the variable pointed to by that parameter. 

Now, let’s move on to the LARGE_INTEGER structure. This is a union that represents a 64-bit signed integer. It was designed to allow use of 64-bit signed integers in C++ compilers lacking built-in support. In fact, its LowPart and HighPart members store the low-order and high-order 32 bits of a 64-bit integer, respectively. 

Its QuadPart member is just a signed 64-bit integer, and since the Visual C++ compiler offers convenient built-in 64-bit integer types (e.g. __int64, or long long), we can use those instead of the LARGE_INTEGER Win32 structure in our code.

Finally, the API returns a BOOL value indicating a success (TRUE) or failure (FALSE) code. We can wrap that API in a more convenient way for use in our C++ code, in particular exposing a functional interface instead of the output pointer parameter, and returning directly a long long type (64-bit integer) instead of using the LARGE_INTEGER union:

inline long long PerformanceCounter() noexcept
{
    LARGE_INTEGER li;
    ::QueryPerformanceCounter(&li);
    return li.QuadPart;
}

Note that the function is marked using C++11’s noexcept, since it doesn’t throw exceptions, and the Visual C++ compiler can do optimizations knowing that. Since the function’s body is short, I’ve defined this function incline. This is a zero-overhead, convenient wrapper around the Win32 QueryPerformanceCounter API.

So, using the custom-defined PerformanceCounter function, you can measure execution time in C++ for a given portion of code like this:

#incldue <Windows.h> // Windows Platform SDK
...

// Record start time
long long start = PerformanceCounter();

// Portion of code to be timed
...

// Record end time
long long finish = PerformanceCounter();

(Note the analogy with std::chrono::high_resolution_clock::now, even though this custom syntax seems clearer.)

How to Compute the Time Interval Measurement

Now that we have recorded the start and finish times, we can compute a difference to get the time interval measurement. The start and finish values are expressed in ticks of the performance counter. This means that if we subtract these values, we get the elapsed number of ticks. Ticks aren’t all that human-friendly, so we’ll want to measure the elapsed time using other units, like seconds or milliseconds.

To do this, we divide the number of elapsed ticks by the number of ticks-per-second, which is the frequency of the performance counter. To get this frequency value, we can use another Windows API called QueryPerformanceFrequency

Here’s the prototype for this:

BOOL WINAPI QueryPerformanceFrequency(
  _Out_ LARGE_INTEGER *lpFrequency
);

As you can see, this prototype structure is exactly the same as QueryPerformanceCounter’s. The main difference here is that the returned output parameter is the frequency of the performance counter (which is measured in ticks-per-second).

Just like the QueryPerformanceCounter API, QueryPerformanceFrequency can be wrapped in a convenient, compact function to use in our C++ code:

inline long long PerformanceFrequency() noexcept
{
    LARGE_INTEGER li;
    ::QueryPerformanceFrequency(&li);
    return li.QuadPart;
}

This frequency is a parameter that is fixed at system boot, so we only need to query for it once. The returned value can be cached for the whole execution of our code. 

How to Compute the Time-Interval Measurement in Milliseconds

Now, because the start and finish time is expressed in ticks and the frequency is expressed in ticks-per-second, we can compute the time-interval measurement in milliseconds using this simple formula:

double elapsedMilliseconds = ((finish - start) * 1000.0) / frequency;

(Note: The parentheses carefully control the order of execution of the operations involved.)

First, the difference between finish and start is computed. Then, this difference is multiplied by a factor of 1,000 to prepare for the final value in milliseconds. Only after this multiplication occurs, the resulting value is divided by the frequency (in ticks-per-second). Following these exact steps helps us to avoid precision losses.

The Stopwatch Class

The above C++/Win32 code can be wrapped in a convenient C++ Stopwatch class and its usage is very simple. Since this is a header-only module, you can just include the “Stopwatch.h” header when you need it, and then use the Stopwatch class like this:

#include "Stopwatch.h"
...

Stopwatch sw;

// Record start time
sw.Start();

// Portion of code to be timed
...

// Record end time
sw.Stop();

std::cout << "Elapsed time: " << sw.ElapsedMilliseconds() << " ms\n";

The code that you want to time is clearly marked between a couple of calls to the Start and Stop methods of the Stopwatch class. Just think of it like holding an actual stopwatch in your hand and pressing the start/stop button to measure a given portion of C++ code. It’s that simple!

(Note: The downloadable code compiles cleanly at warning level 7 (/W4) with Visual C++ 2015, in both 32-bit and 64-bit builds.)

Takeaway

You now have a few techniques in your toolkit that can be used to measure execution time in C++. Remember, this can come in handy when:

  • Trying to determine how much time is spent in some portions of C++ code

  • Contrasting and comparing several algorithms and choices of data to optimize for execution speed

Learn more about C++ from Pluralsight!

Get our content first. In your inbox.

Loading form...

If this message remains, it may be due to cookies being disabled or to an ad blocker.

Contributor

Giovanni Dicanio

Giovanni Dicanio is a computer programmer specializing in C, C++ and the Windows OS. He is a Pluralsight author and Visual C++ MVP, with computer programming experience dating back to the glorious Commodore 64 and Commodore Amiga 500 golden days. Giovanni enjoys several aspects of development: from design to actual code writing, to debugging and code reviews. Feel free to contact him at giovanni.dicanio AT gmail.com or visit his website. Besides programming and course authoring, he enjoys helping others on forums and communities devoted to C++.