How to measure execution time intervals in C++

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

As a C++ developer, it can be incredibly helpful knowing how to measure execution times for certain portions of code. 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’ll start by covering a few classes (available since C++11) that are useful when dealing with time-interval measurements. After that, we’ll take a look at how to implement things under the hood using direct calls to Windows APIs.

Tools from the C++ standard library

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: first at the beginning portion of the code, and again at the end of that portion. Using this method, we record the start and the end 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 this 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 C++11’s auto keyword to help reduce that clutter and increase code readability.

Once you’ve recorded the start and end time of a given portion of code, you can use another class of the same std::chrono namespace to get the elapsed time (the name of this class is duration, and it represents a time interval). You can construct an instance of this class using the start and end 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";

Now, before we go any further, let’s do a quick recap:

1. Call high_resolution_clock::now at the start and end point of the portion of code to measure.

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

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

Windows high-resolution time stamps

Now that you’ve seen how to measure time intervals using tools available in C++11’s standard library, let’s have a look under the hood. We’ll see how these measurements can be performed on the Windows platform in native user-mode C++ code.

To start, there are a couple of Windows APIs you’ll want to note. The first is QueryPeformanceCounter. As stated by its MSDN documentation, 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 time intervals, similar to what we previously saw with the high_resolution_clock::now method.

Again, the idea is to call QueryPerformanceCounter twice: first at the start of the portion of code, and again at the end. After the start and end 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, and it means that the lpPerformanceCounter 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. If you aren’t familiar with SAL, an excellent introduction can be found in this Microsoft article.

Now, let’s move on to the LARGE_INTEGER structure. This is a union that represents a 64-bit signed integer, and 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 inline. This is a zero-overhead, convenient wrapper around the Win32 QueryPerformanceCounter API.

If you’re using an older version of the Visual C++ compiler, you can substitute noexcept with “throw()” (this is a Visual C++-specific non-standard extension to the C++ language, preceding C++11’s noexcept). They have some different meanings, but the bottom line is that the MSVC compiler can optimize code if it knows that a function doesn’t throw exceptions.

So, using the custom-defined PerformanceCounter function, you can record the start and end time of a given portion of C++ 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.)

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

With this in mind, we can 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 of QueryPerformanceCounter’s. The main difference here is that the returned output parameter is the frequency of the performance counter. And remember, the frequency 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, and the returned value can be cached for the whole execution of our code. Now, because the start and finish time expressed in ticks, and the frequency 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 occurrs, the resulting value is divided by the frequency (in ticks-per-second). Following these exact steps helps us to avoid precision losses.

This C++/Win32 code can be wrapped in a convenient class, in a way similar to the C#/.NET’s Stopwatch class. To view and use a simple C++ Stopwatch class that I developed, head over to GitHub. 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 4 (/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 intervals in C++. Remember, this can come in handy when trying to determine how much time is spent in some portions of C++ code. It’s also great for contrasting and comparing several algorithms and choices of data structures to optimize for execution speed. To learn more about some applications of C++, check out my courses here

Get our content first. In your inbox.

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++.