Jak korzystać z QueryPerformanceCounter?
Niedawno zdecydowałem, że muszę zmienić z używania milisekund na mikrosekundy dla mojej klasy timera, i po kilku badaniach zdecydowałem, że QueryPerformanceCounter jest prawdopodobnie moim najbezpieczniejszym zakładem. (Ostrzeżenie na Boost::Posix
, że może nie działać na API Win32 trochę mnie zniechęciło). Jednak nie jestem pewien, jak to wdrożyć.
To, co robię, to wywołanie dowolnej GetTicks()
funkcji esque, której używam i przypisanie jej do zmiennej timera startingTicks
. Wtedy, aby znaleźć ilość czasu, który upłynął, po prostu odejmij wartość zwracaną funkcji od startingTicks
, a kiedy zresetuję timer, po prostu ponownie wywołuję funkcję i przypisuję do niej startingTicks. Niestety, z kodu, który widziałem, nie jest tak proste, jak tylko wywołanie QueryPerformanceCounter()
, i nie jestem pewien, co mam przekazać jako jego argument.
4 answers
#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;
}
Ten program powinien wypisywać liczbę zbliżoną do 1000(Windows sleep nie jest tak dokładny, ale powinien być podobny do 999).
Funkcja StartCounter()
rejestruje liczbę wskazów, które licznik wydajności posiada w zmiennej CounterStart
. Funkcja GetCounter()
Zwraca liczbę milisekund od ostatniego wywołania StartCounter()
jako podwójnej, więc jeśli GetCounter()
zwraca 0.001, to od wywołania StartCounter()
minęło około 1 mikrosekundy.
Jeśli chcesz, aby timer zamiast tego używał sekund, to Zmień
PCFreq = double(li.QuadPart)/1000.0;
Do
PCFreq = double(li.QuadPart);
Lub jeśli chcesz mikrosekundy to użyj
PCFreq = double(li.QuadPart)/1000000.0;
Ale tak naprawdę chodzi o wygodę, ponieważ Zwraca podwójną.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-23 14:44:14
Używam tych definicji:
/** 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;
Użycie (w nawiasach, aby zapobiec przedefiniowaniu):
TIMER_INIT
{
TIMER_START
Sleep(1000);
TIMER_STOP
}
{
TIMER_START
Sleep(1234);
TIMER_STOP
}
Wyjście z przykładu użycia:
1.00003 sec
1.23407 sec
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-07 23:46:51
Zakładając, że jesteś na Windows (jeśli tak, powinieneś oznaczyć swoje pytanie jako takie!), na tej stronie MSDN znajdziesz źródło prostej, użytecznej klasy HRTimer
C++, która zawija wymagane wywołania systemowe, aby zrobić coś bardzo zbliżonego do tego, czego potrzebujesz(łatwo byłoby dodać do niej metodę GetTicks()
, w szczególności, aby wykonać dokładnie to, czego potrzebujesz).
Na platformach innych niż Windows nie ma funkcji QueryPerformanceCounter, więc rozwiązanie nie będzie bezpośrednio przenośne. Jednakże, jeśli nie owijasz go w klasę taką jak wyżej wspomniana HRTimer
, łatwiej będzie zmienić implementację klasy, aby korzystać z tego, co obecna platforma jest w stanie zaoferować (może przez Boost lub cokolwiek!).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-15 23:28:25
Chciałbym rozszerzyć to pytanie o przykład sterownika NDIS na uzyskanie czasu. Jak wiadomo, KeQuerySystemTime (naśladowany pod ndisgetcurrentsystemtime) ma niską rozdzielczość powyżej milisekund i istnieją pewne procesy, takie jak pakiety sieciowe lub inne IRP, które mogą potrzebować lepszego znacznika czasu; {]}
Przykład jest równie prosty:
LONG_INTEGER data, frequency;
LONGLONG diff;
data = KeQueryPerformanceCounter((LARGE_INTEGER *)&frequency)
diff = data.QuadPart / (Frequency.QuadPart/$divisor)
Gdzie dzielnik wynosi 10^3 lub 10^6 w zależności od wymaganej rozdzielczości.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-21 14:12:12