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.

Author: Evan Carslake, 0000-00-00

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ą.

 152
Author: Ramónster,
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
 18
Author: vent,
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!).

 2
Author: Alex Martelli,
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.

 1
Author: kagali-san,
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