如何使用 QueryPerformanceCounter?

我最近决定,我需要从使用毫秒改为微秒为我的定时器类,经过一些研究,我已经决定,QueryPerformanceCounter 可能是我最安全的赌注。(Boost::Posix上关于它可能无法在 Win32API 上工作的警告让我有点不爽)。然而,我真的不知道如何实现它。

我所做的就是调用我正在使用的 GetTicks()风格函数,并将其赋值给 Timer 的 startingTicks变量。然后,为了计算所花费的时间,我只需要从 startingTicks中减去函数的返回值,当我重置计时器时,我只需要再次调用函数并为它分配 startingTicks。不幸的是,从我看到的代码来看,它并不像调用 QueryPerformanceCounter()那样简单,而且我不确定我应该传递什么作为它的参数。

199478 次浏览
#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.

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!).

I would extend this question with a NDIS driver example on getting time. As one knows, KeQuerySystemTime (mimicked under NdisGetCurrentSystemTime) has a low resolution above milliseconds, and there are some processes like network packets or other IRPs which may need a better timestamp;

The example is just as simple:

LONG_INTEGER data, frequency;
LONGLONG diff;
data = KeQueryPerformanceCounter((LARGE_INTEGER *)&frequency)
diff = data.QuadPart / (Frequency.QuadPart/$divisor)

where divisor is 10^3, or 10^6 depending on required resolution.

I use these defines:

/** Use to init the clock */
#define TIMER_INIT \
LARGE_INTEGER frequency; \
LARGE_INTEGER t1,t2; \
double elapsedTime; \
QueryPerformanceFrequency(&frequency);




/** Use to start the performance timer */
#define TIMER_START QueryPerformanceCounter(&t1);


/** Use to stop the performance timer and output the result to the standard stream. Less verbose than \c TIMER_STOP_VERBOSE */
#define TIMER_STOP \
QueryPerformanceCounter(&t2); \
elapsedTime=(float)(t2.QuadPart-t1.QuadPart)/frequency.QuadPart; \
std::wcout<<elapsedTime<<L" sec"<<endl;

Usage (brackets to prevent redefines):

TIMER_INIT


{
TIMER_START
Sleep(1000);
TIMER_STOP
}


{
TIMER_START
Sleep(1234);
TIMER_STOP
}

Output from usage example:

1.00003 sec
1.23407 sec