GetTickCount (has granularity of ~15 msec which is bad)
timeGetTime depends on last period set using with timeBeginPeriod,
the docs say it is system-global, so if an app uses timeBeginPeriod(40)
while your program is running, you immediately get worse granularity
than GetTickCount. Also it is said that this affects thread scheduling so better keep hands off it.
QueryPerformanceCounter/QueryPerformanceFrequency:
ultra high resolution, if i remember these calls used to take longer than GetTickCount. Not always available.
rdtsc instruction: very fast but there may be problems with multiple cores and dynamic changes in CPU freq, nonportable
Are there better alternatives I missed?
Now what if I wanted to measure in microseconds instead of some units (usually close to CPU clock freq) which QPF provides?
Here's my solution using QPC/QPF. Note that it's fairly slow on a 32-bit machine here, getMicrosec() itself takes about 1-2 microseconds here () which is already a lot.
Anyway, here's my solution with implementation in case it's useful to someone: (left out some impl. details but I guess it's self-explanatory)
Code: Select all
static volatile signed char init = 0;
static i64 freq;
static u32 shortFreq;
static i64 lastTick;
static i64 remainder = 0;
static i64 emul = 0;
static Mutex usMutex;
static Mutex gMutex;
static i32 lastTC;
i64 getMicrosec()
{
if ( init == -1 )
{
LARGE_INTEGER tmp;
QueryPerformanceCounter( &tmp );
i64 cur = (i64)tmp.QuadPart;
{
MutexLock _( usMutex );
i64 delta = cur - lastTick + remainder;
i64 us = delta * 1000000 / shortFreq;
emul += us;
remainder = delta - us * freq / 1000000;
lastTick = cur;
return emul;
}
}
if ( init == 1 )
{
LARGE_INTEGER tmp;
QueryPerformanceCounter( &tmp );
i64 cur = (i64)tmp.QuadPart;
{
MutexLock _( usMutex );
i64 delta = cur - lastTick + remainder;
i64 us = delta * 1000000 / freq;
emul += us;
remainder = delta - us * freq / 1000000;
lastTick = cur;
return emul;
}
}
if ( init == 2 )
{
MutexLock _( usMutex );
i32 cur = (i32)GetTickCount();
i32 delta = cur - lastTC;
i64 tmp = (i64)delta * 1000;
lastTC = cur;
return emul += tmp;
}
// initialize
MutexLock _( gMutex );
LARGE_INTEGER frq;
if ( QueryPerformanceFrequency( &frq ) == FALSE )
{
// not available => use milisec emulation
init = 2;
lastTC = GetTickCount();
} else {
freq = (i64)frq.QuadPart;
LARGE_INTEGER tmp;
QueryPerformanceCounter( &tmp );
lastTick = (i64)tmp.QuadPart;
if ( freq <= 0xffffffffU )
{
shortFreq = (u32)freq;
init = -1;
}
else
{
init = 1;
}
}
return getMicrosec();
}
i32 getMillisec()
{
return (i32)(getMicrosec()/1000 & 0xffffffffU);
}
Of course if perfcounter is not available the code falls back to emulation using GetTickCount().