tags:

views:

1902

answers:

2

I recently decided that I needed to change from using milliseconds to microseconds for my Timer class, and after some research I've decided that QueryPerformanceCounter is probably my safest best. (The warning on Boost::Posix that it may not works on Win32 API put me off a bit). However, I'm not really sure how to implement it.

What I'm doing is calling whatever GetTicks() esque function I'm using and assigning it to Timer's startingTicks variable. Then to find the amount of time passed I just subtract the function's return value from the startingTicks, and when I reset the timer I just call the function again and assign startingTicks to it. Unfortunately, from the code I've seen it isn't as simple as just calling QueryPerformanceCounter(), and I'm not sure what I'm supposed to pass as its argument.

Any assistance is appreciated, thanks.

+4  A: 
#include <windows.h>

double PCFreq = 0.0;
__int64 CounterStart = 0;

void StartCounter()
{
    LARGE_INTEGER li;
    if(!QueryPerformanceFrequency(&li))
 cout << "QueryPerformanceFrequency failed!\n";

    PCFreq = double(li.QuadPart)/1000.0;

    QueryPerformanceCounter(&li);
    CounterStart = li.QuadPart;
}
double GetCounter()
{
    LARGE_INTEGER li;
    QueryPerformanceCounter(&li);
    return double(li.QuadPart-CounterStart)/PCFreq;
}

int main()
{
    StartCounter();
    Sleep(1000);
    cout << GetCounter() <<"\n";
    return 0;
}

This program should output a number close to 1000 (windows sleep isn't that accurate, but it should be like 999).

The StartCounter() function records the number of ticks the performance counter has in the CounterStart variable. The GetCounter() function returns the number of milliseconds since StartCounter() was last called as a double, so if GetCounter() returns 0.001 then it has been about 1 microsecond since StartCounter() was called.

If you want to have the timer use seconds instead then change

PCFreq = double(li.QuadPart)/1000.0;

to

PCFreq = double(li.QuadPart);

or if you want microseconds then use

PCFreq = double(li.QuadPart)/1000000.0;

But really it's about convenience since it returns a double.

Ramónster
lol, I just coded this a few days ago
Ramónster
Exactly, what is LARGE_INTEGER?
Anonymous
it's a windows type, basically a portable 64 bit integer. It's definition depends on whether the target system supports 64 bit integers or not. If the system doesn't support 64 bit ints then it's defined as 2 32 bit ints, a HighPart and a LowPart. If the system does support 64 bit ints then it's a union between the 2 32 bit ints and a 64 bit int called the QuadPart.
Ramónster
Any QueryPerformanceCounter returns the # of microseconds?
Anonymous
Well it returns the number of ticks on the high resolution system clock, which I think is hardware dependant, and that's why you need to use QueryPerformanceFrequency to figure out the frequency of that clock. On my system the frequency is 2,272,000 hz, or about 2.272 ticks per microsecond.
Ramónster
And I need to include Windows.h?
Anonymous
yes, you do need to include windows.h
Ramónster
So based on your explanation, it returns the timing in milliseconds, but with microsecond precision?
Anonymous
It's precision is actually based on your system's timer, it should be a little more accurate than microseconds. But yes, the example as I list it will work fine for microsecond timing, or you can change it so 1.0 means 1 microsecond if you would rather think about microseconds.
Ramónster
I'm getting the time in seconds, but I need microsecond precision. I was using a function that returned the # of milliseconds, which I just divided by 1000 to get the time in seconds, but since I now need microseconds I either needed a timer that returned the # of microseconds or some other unit with microsecond precision. Going to try to implement it now.
Anonymous
I'm getting extremely small values (e-312). I'm reading around and does being on a laptop have something to do with it?
Anonymous
Where are you getting small values?
Ramónster
When I perform m_StartingTime - li.QuadPart (no dividing because from what you showed above it seems that the time is in seconds with a precision of microseconds).
Anonymous
if you do something likeStartCounter();cout << GetCounter();then you should get a small value, because there wasn't much time between StartCounter() and GetCounter(). If you doStartCounter();Sleep(1000);cout << GetCounter();then it should be about 1 second.
Ramónster
Well you still need the division, or else you're just going to get the number of ticks the counter has done since m_StartingTime, but you want a standard unit of time.
Ramónster
If you do the division with integers though you're going to lose precision. The functions I posted handle this correctly, and in my answer I say how to modify them to use seconds as the unit but still giving high precision.
Ramónster
Ah I understand what the frequency is for now. That definitely worked, but now the difference is 975 which is way too high. I don't think what you described returns in seconds with precision to microseconds. I think it returns # of microseconds.
Anonymous
My functions as listed return milliseconds, but I say how to modify the StartCounter() function to return seconds with high precision in the answer.
Ramónster
Well for example you said to use seconds change the /1000 to just assigning PCFreq to the QueryPerformanceFrequency. When I do that I get numbers in the hundreds so that obviously can't be right.
Anonymous
I'm also not using Sleep().
Anonymous
Try the code sample in my answer, I edited it so if the call to QueryPerformanceFrequency fails then it prints to the screen.
Ramónster
I don't think it's failing. I've decided I'm just going to stick with milliseconds and round up to 1 millisecond whenever it's 0. The QueryPerformanceCounter is giving very unstable results, i.e the frames are very jumpy and not smooth at all.
Anonymous
A: 

Assuming you're on Windows (if so you should tag your question as such!), on this MSDN page you can find the source for a simple, useful HRTimer C++ class that wraps the needed system calls to do something very close to what you require (it would be easy to add a GetTicks() method to it, in particular, to do exactly what you require).

On non-Windows platforms, there's no QueryPerformanceCounter function, so the solution won't be directly portable. However, if you do wrap it in a class such as the above-mentioned HRTimer, it will be easier to change the class's implementation to use what the current platform is indeed able to offer (maybe via Boost or whatever!).

Alex Martelli