Introduction
When developing a game or application it is most likely that you will come in contact with time.
Example: frame independent rendering
In this article I'll show you how to create and use a 'High-Performance Timer' and a 'General Tick Timer'
Getting Time data
We could use the clock() function from <time.h> which returns the approximation of processor time.
But clock() can't register very short processor times.
So we must use a system dependent timer.
High-'performance' timer
This is a microsecond precision timer,
On the windows platform we could use
BOOL WINAPI QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);
Expand/hide Windows High-performance timer
Using the QueryPerformanceCounter() functions require only the windows include
#include <windows.h>
First off all we need to find out its accuracy, processor Frequency:
double dFrequency = 0;
LARGE_INTEGER iTicksPerSecond;
if(QueryPerformanceFrequency(&iTicksPerSecond) == TRUE)
{
dFrequency = 1.0/iTicksPerSecond.QuadPart;
}
then get amount of tick's that has elapsed since the system was booted.
LARGE_INTEGER iTick;
long long iTickCount = 0;
if(QueryPerformanceCounter(&iTick) == TRUE)
{
iTickCount = iTick.QuadPart;
}
now we need to convert the TickCount to a time value in seconds
double dElapsedTime = iTickCount * dFrequency;
We could get different results if your PC has multiple CPU cores, because QueryPerformanceCounter() is not core specific.
So if you can’t guarantee which core it will use.
We can force the thread to run on a specified core.
By specifying we are using the first CPU core for the performance timer, our code runs also perfectly on a single core PC.
This way we are avoiding the problem of deferent CPU Clock Ticks.
//force the thread to our first processor
DWORD_PTR dwOldAffinityMask = SetThreadAffinityMask(GetCurrentThread(), 0x01);
//...do our thread specific stuff
//and set the thread back
if(dwOldAffinityMask != NULL)SetThreadAffinityMask(GetCurrentThread(), dwOldAffinityMask);
Switching the affinity mask for a process or thread can result in threads receiving less processor time,
but QueryPerformanceCounter is also not the fastest function on earth...
And they should only be uses when absolutely necessary, like for profiling.
on Linux we should use
int clock_gettime(clockid_t clock_id, struct timespec *tp);
Expand/hide Linux High-performance timer
the famous function gettimeofday() gives us a lot of information.
But it is depending on the system clock time, so if the user is changing it or daylight saving kicks in...
The elapsed time will be inaccurate, it could even be negative,...
thats the main reason to use clock_gettime().
Header to include:
#include <sys/time.h>
the time timespec struct is defined like this:
struct timespec {
time_t tv_sec; //seconds
long tv_nsec; //nanoseconds
};
getting the time data isn't that hard
long long iElapsedTime=0;
struct timespec TimeData;
if( clock_gettime( CLOCK_PROCESS_CPUTIME_ID, &TimeData) != -1 )iElapsedTime = TimeData.tv_nsec;
And we could convert it to seconds to fit with the rest of the code.
double dElapsedTime = iElapsedTime / 1000000000.0;
and on a Mac (apple) platform uint64_t mach_absolute_time();
Expand/hide Apple High-performance timer
Identically like on the Linux platform gettimeofday(2) is unsafe to use because it depends on the system time and this can be changed by the user, daylight saving,leap seconds get added,...
The clock_gettime(2) is also a 'no go' simply because it is not supported on all versions.
Our includes are:
#include <stdint.h>
#include <mach/mach_time.h>
#include <mach/kern_return.h>
First off all we need to find out its accuracy, processor Frequency:
double dFrequency = 0;
mach_timebase_info_data_t tinfo;
if( mach_timebase_info(&tinfo) == KERN_SUCCESS)
{
dFrequency = 1.0 / static_cast<double>(tinfo.number / tinfo.denom);
}
Get the number of ticks since the last boot.
long long iElapsedTime = mach_absolute_time();
now the only thing need to do, is convert the tick count to seconds
double dElapsedTime = iTickCount * dFrequency;
When combined, the cross-platform timer class souled look like this:
Show complete Timer class Source
Timer.h//-----------------------------------------------------------------
// C++ Header - Timer.h
// Copyright© Laurens Vandenbroucke - http://www.iCorp.be
//------
//Classes
//-PerformanceTimer
//-----------------------------------------------------------------
#ifndef _TIMER_H
#define _TIMER_H
//-----------------------------------------------------------------
// Class PerformanceTimer
// a nanosecond precision timer,
// useful for benchmarks / profiling
//-----------------------------------------------------------------
class PerformanceTimer
{
public:
PerformanceTimer(); // Constructor
virtual ~PerformanceTimer(); // Destructor
//-------------------------------------------------
// Methods
//-------------------------------------------------
/*This will start the timer*/
void Start();
/**/
void Stop();
/**/
void Reset();
/* Returns the elapsed time between the last
* 'Start() / Reset()' call and now.
* if stop was called the stop time will be used*/
double GetElapsedTime()const;
private:
//-------------------------------------------------
// private functions
//-------------------------------------------------
//returns our current time data
long long GetTime()const;
//-------------------------------------------------
// Datamembers
//-------------------------------------------------
long long m_iStartTime,m_iEndTime;
double m_dFrequency;
// -------------------------
// Disabling default copy constructor and default assignment operator.
PerformanceTimer(const PerformanceTimer& p);
PerformanceTimer& operator=(const PerformanceTimer& p);
// -------------------------
};
#endif //_TIMER_H
Timer.cpp
//-----------------------------------------------------------------
// C++ Source - Timer.cpp
// Copyright© Laurens Vandenbroucke - http://www.icorp.be
//------
//
//-----------------------------------------------------------------
//-----------------------------------------------------------------
//-----Declarations----
//-----------------------------------------------------------------
//-----Includes----
#include "Timer.h"
#ifdef _WIN32
#include <windows.h>
#elif defined __unix__
#include <sys/time.h>
#elif defined __APPLE__
#include <stdint.h>
#include <mach/mach_time.h>
#include <mach/kern_return.h>
#endif
//-----------------------------------------------------------------
// Class PerformanceTimer
//-----------------------------------------------------------------
PerformanceTimer::PerformanceTimer()
:m_iStartTime(0), m_iEndTime(0), m_dFrequency(0)
{
//constructor
#ifdef _WIN32
LARGE_INTEGER iTicksPerSecond;
//force the thread to our first processor
DWORD_PTR dwOldAffinityMask = SetThreadAffinityMask(GetCurrentThread(), 0x01);
if(QueryPerformanceFrequency(&iTicksPerSecond) == TRUE)
{
m_dFrequency = 1.0 / iTicksPerSecond.QuadPart;
}
//and set the thread back
if(dwOldAffinityMask != NULL)
SetThreadAffinityMask(GetCurrentThread(), dwOldAffinityMask);
#elif defined __unix__
dFrequency = 0.000000001;//used for converting nanosecond to second Second=nanosecond*(1e-9)
#elif defined __APPLE__
mach_timebase_info_data_t tinfo;
if( mach_timebase_info(&tinfo) == KERN_SUCCESS)
{
dFrequency = 1.0 / static_cast<double>(tinfo.number / tinfo.denom);
}
#endif
}
PerformanceTimer::~PerformanceTimer()
{
// destructor
}
//---------------------------
// Functions
//---------------------------
void PerformanceTimer::Start()
{
m_iEndTime = 0;
m_iStartTime = GetTime();
}
void PerformanceTimer::Stop()
{
m_iEndTime = GetTime();
}
void PerformanceTimer::Reset()
{
Start();
}
double PerformanceTimer::GetElapsedTime() const
{
double fElapsedTime = 0;
if(m_iEndTime != 0)
{
fElapsedTime = static_cast<double>(m_iEndTime - m_iStartTime);
}
else fElapsedTime = static_cast<double>(GetTime() - m_iStartTime);
return (fElapsedTime * m_dFrequency);
}
//---------------------------
// Private Functions
//---------------------------
long long PerformanceTimer::GetTime() const
{
long long iTime = 0;
#ifdef _WIN32
//force the thread to our first processor
DWORD_PTR dwOldAffinityMask = SetThreadAffinityMask(GetCurrentThread(), 0x01);
//get the tick count and use it as our time data
LARGE_INTEGER iTickCount;
if(QueryPerformanceCounter(&iTickCount) == TRUE)iTime = iTickCount.QuadPart;
//and set the thread back
if(dwOldAffinityMask != NULL)SetThreadAffinityMask(GetCurrentThread(), dwOldAffinityMask);
#elif defined __unix__
struct timespec TimeData;
if( clock_gettime( CLOCK_PROCESS_CPUTIME_ID, &TimeData) != -1 )
iTime = TimeData.tv_nsec;
#elif defined __APPLE__
iTime = mach_absolute_time();
#endif
return iTime;
}
General 'Tick' Timer
A Tick Timer is a fast timer with millisecond precision.
Expand/hide Windows Tick timer
Using windows functions require the windows include:
#include <windows.h>
On windows you can get the elapsed tick count by calling
DWORD GetTickCount();
or the newer
ULONGLONG GetTickCount64();
but i prefer the Windows Multimedia function
DWORD timeGetTime();
it is a bit slower than the GetTickCount(), but we got better precision and the returned value is in milliseconds.
to use this function you need to link the Windows Multimedia library "Winmm.lib"
When using visual studio you can simply use a pragma comment
#pragma comment(lib,"Winmm.lib")
If your developing an application thats has no need for a constant update() call, u can use the win32 timer function:
UINT_PTR SetTimer(HWND hWindow, UINT_PTR nIDEvent,UINT uElapse,TIMERPROC lpTimerFunc);
It will notify you
uElapse
milliseconds after the call.
this is a handy procedure if you don't have a update() build in, were you can verify the elapsed time.
Example:
when you create a text editor, an update() function would be completely unnecessary.
Unless you want recalculate everything multiple times a second, like a game.
When the uElapse
time is reached, a notification will be sent and the countdown will start again until you call:
BOOL KillTimer(HWND hWindow,UINT_PTR uIDEvent);
You can receive the timer callback notification in 2 different ways:
By the specified window message procedure
#include <windows.h>
#define IDT_TIMER1 1 //the unique timer id
bool CreateMyTimer()
{
if(SetTimer(hWindow, //the HWND handle of window which will receive the WM_TIMER msg
IDT_TIMER1, // timer identifier
60*1000, // 60 seconds interval
NULL) == 0)return false;
return true;
}
Using a Callback Function
#include <windows.h>
#define IDT_TIMER1 1 //the timer id
void TimerProc(HWND hWindow, UINT uMsg, UINT_PTR TimerID, DWORD dwTime)
{
KillTimer(hWindow, TimerID); //destroy the timer on the first callback
}
bool CreateMyTimer()
{
if(SetTimer(NULL,
IDT_TIMER1, // timer identifier
60*1000, // 60 seconds interval
(TIMERPROC)TimerProc)==0)return false;
return true;
}
Remarks
a high performance timer is preciser but also slower, it should only be used on profiling tasks.
For all the other time measurement you should use a "General use timer", its less precise but it will do the task.
Notice: Timers could return incorrect results due system error, overload, hibernation,...
So check always if your results is within a acceptable range.
void Tick()
{
const float fMaxTimeDiff = 2.5f;
//get the time in sec between the last Reset/start call and now
float fTimeDiff = m_TimerPtr->GetElapsedTime();
//making sure that the elapsed time is not exceeding the Maximum Time Difference
if(fTimeDiff > fMaxTimeDiff)fTimeDiff = fMaxTimeDiff;
//calling a function with the elapsed time as param
Update(fTimeDiff);
m_TimerPtr->Reset();
}