Login


C++ High-Resolution Timer

By Jonathan Wood on 12/22/2010
Language: C++
Technology: MFC
Platform: Windows
License: CPOL
Views: 19,108
General Programming » Time & Date » Timers » C++ High-Resolution Timer

Demo Project Screenshot

Download Test Project Download Test Project

Introduction

Timers serve many purposes for computer programs. They can be used to track time, schedule events, and as a stopwatch to measure how long an activity takes.

However, when it comes to measuring how long code takes to run, many traditional timers lack the resolution to measure fractions of a second in a meaningful way.

For these cases, Windows provides the QueryPerformanceCounter() API function, which returns a very high-resolution value that can be used to time short intervals.

The CHRTimer Class

Listing 1 shows my CHRTimer class. This class is quite simple and exists entirely within a header file (no CPP file).

The class relies on the QueryPerformanceCounter() API function to measure short intervals of time. Internally, it has two LARGE_INTEGER member variables, m_liStart and m_liStop.

The Start() method stores the current counter value in m_liStart, and the Stop() method stores the current value in m_liStop. After calling both methods, an application can call GetElapsed() to get the number of counts between the start and stop times.

The GetElapsedAsSeconds() method is also provided. This method uses the same value returned by GetElapsed() and then divides that value by the number returned by the QueryPerformanceFrequency() API function. This function returns the number of counts that occur per second on the current system.

Listing 1: The CHRTimer Class

#pragma once

#include <Windows.h>

class CHRTimer
{
protected:

    LARGE_INTEGER m_liStart;
    LARGE_INTEGER m_liStop;

public:

    CHRTimer(void)
    {
        m_liStart.QuadPart = m_liStop.QuadPart = 0;
    }

    ~CHRTimer(void)
    {
    }

    // Starts the timer
    void Start()
    {
        ::QueryPerformanceCounter(&m_liStart);
    }

    // Stops the timer
    void Stop()
    {
        ::QueryPerformanceCounter(&m_liStop);
    }

    // Returns the counter at the last Start()
    LONGLONG GetStartCounter()
    {
        return m_liStart.QuadPart;
    }

    // Returns the counter at the last Stop()
    LONGLONG GetStopCounter()
    {
        return m_liStop.QuadPart;
    }

    // Returns the interval between the last Start() and Stop()
    LONGLONG GetElapsed()
    {
        return (m_liStop.QuadPart - m_liStart.QuadPart);
    }

    // Returns the interval between the last Start() and Stop() in seconds
    double GetElapsedAsSeconds()
    {
        LARGE_INTEGER liFrequency;
        ::QueryPerformanceFrequency(&liFrequency);
        return ((double)GetElapsed() / (double)liFrequency.QuadPart);
    }
};

Using the Class

As described previously, to use this class simply call the Start() method, then perform the activity you want to time, and then call the Stop() method. You can then call the GetElapsed() and/or GetElapsedAsSeconds() methods to get the timer measurement.

Listing 2 demonstrates this sequence.

Listing 2: Using the CHRTimer Class

// Create timer
CHRTimer timer;

// Start the timer
timer.Start();

// Do some processing
// ...

// Stop the timer
timer.Stop();

// Get time elapsed
timer.GetElapsed();
timer.GetElapsedAsSeconds();

Words of Warning

I should point out the while these routines can distinguish between very small time intervals, they are not accurate time keepers. That is, they are very good at detecting tiny variations in lengths of time, but if they report something took exactly one and a half seconds, that information is less reliable.

One reason for this is because not all computers provide the same hardware for QueryPerformanceCounter() to work with. The timer used can vary widely between systems. The GetPerformanceFrequency() function helps normalize for different hardware but variations between systems may still occur.

I've also read reports that these timer calculations can, in rare instances, vary the pace or even go backwards for short periods. And finally, the documentation is not very clear about when the timer start. Presumably, it could wrap around at some point. Although, being a 64-bit value, that's not going to happen very often.

The bottom line is that this code is more meaningful when comparing different intervals on the same system. And it's always a good idea to get multiple readings before making big changes to tweak your code.

Conclusion

With my warnings out of the way, I've found this to be a very useful class capable of measuring very small time intervals. It works really well for measuring the time it takes for some block of code to execute. And it's very simple to implement and use.

End-User License

Use of this article and any related source code or other files is governed by the terms and conditions of The Code Project Open License.

Author Information

Jonathan Wood

I'm a software/website developer working out of the greater Salt Lake City area in Utah. I've developed many websites including Black Belt Coder, Insider Articles, and others.