2009-11-15 22 views
88

Niedawno zdecydowałem, że muszę zmienić czas używania milisekund na mikrosekundy dla mojej klasy Timer, a po pewnych badaniach zdecydowałem, że QueryPerformanceCounter jest prawdopodobnie najbezpieczniejszym zakładem. (Ostrzeżenie na Boost::Posix, że może nie działać na Win32 API, odstraszyło mnie trochę). Jednak nie jestem pewien, jak to wdrożyć.Jak korzystać z QueryPerformanceCounter?

To, co robię, to wywołanie dowolnej funkcji esque, której używam, i przypisanie jej do zmiennej czasowej Timera. Następnie, aby znaleźć ilość czasu, po prostu odejmuję wartość zwracaną przez funkcję z startingTicks, a po zresetowaniu licznika po prostu wywołuję funkcję ponownie i przypisuję do niej parametr startTicks. Niestety, z kodu, który widziałem, nie jest tak prosty jak zwykłe wywoływanie QueryPerformanceCounter() i nie jestem pewien, co mam przekazać jako jego argument.

+2

Mam podjętej kod urywki Ramonster i zamieniłem ich w bibliotekę tutaj: https://gist.github.com/1153062 dla obserwujących. – rogerdpack

+3

Niedawno zaktualizowaliśmy dokumentację QueryPerformanceCounter i dodaliśmy dodatkowe informacje dotyczące właściwego użycia i odpowiedzi na często zadawane pytania. Zaktualizowaną dokumentację można znaleźć tutaj: http://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx –

+0

, tak jak wspomnieć o [__rdtsc] (https: // msdn.microsoft.com/en-us/library/twchhe95.aspx), to właśnie używa QueryPerformanceCounter. –

Odpowiedz

144
#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 wypisać liczbę bliską 1000 (tryb uśpienia nie jest taki dokładny, ale powinien być podobny do 999).

Funkcja StartCounter() rejestruje liczbę znaczników, które licznik wydajności ma w zmiennej CounterStart. Funkcja GetCounter() zwraca liczbę milisekund, ponieważ StartCounter() była ostatnio nazywana podwójną, więc jeśli GetCounter() zwróci 0,001, to wynosi około 1 mikrosekundę, ponieważ wywołano StartCounter().

Jeśli chcesz mieć użyciu Timer sekund zamiast następnie zmienić

PCFreq = double(li.QuadPart)/1000.0; 

do

PCFreq = double(li.QuadPart); 

lub jeśli chcesz mikrosekund następnie użyć

PCFreq = double(li.QuadPart)/1000000.0; 

Ale tak naprawdę to chodzi o wygodę ponieważ zwraca podwójną.

+0

lol, właśnie zakodowałem to kilka dni temu –

+2

Dokładnie, czym jest LARGE_INTEGER? – Anonymous

+3

to typ okna, w zasadzie przenośna 64-bitowa liczba całkowita. Jego definicja zależy od tego, czy system docelowy obsługuje 64-bitowe liczby całkowite, czy też nie. Jeśli system nie obsługuje 64-bitowych intów, definiuje się je jako 2 32-bitowe ints, HighPart i LowPart. Jeśli system obsługuje 64-bitowe ints, jest to połączenie między 2 32-bitowymi intami a 64-bitowymi int nazwanymi QuadPart. –

2

Zakładając, że jesteś na systemie Windows (jeśli tak, należy oznaczyć pytanie jako takiego!), Na this MSDN page można znaleźć źródło prostego, użytecznego HRTimer klasy C++, który owija potrzebny system wywołuje zrobić coś bardzo blisko czego potrzebujesz (łatwo byłoby dodać do niego 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. Jeśli jednak umieścisz go w klasie, takiej jak wyżej wymieniony HRTimer, łatwiej będzie zmienić implementację klasy, aby wykorzystać to, co aktualnie oferuje platforma (być może poprzez Boost lub cokolwiek!).

1

Chciałbym rozszerzyć to pytanie za pomocą przykładu sterownika NDIS dotyczącego czasu. Jak wiadomo, KeQuerySystemTime (naśladowany przez NdisGetCurrentSystemTime) ma niską rozdzielczość powyżej milisekund, a istnieją pewne procesy, takie jak pakiety sieciowe lub inne IRP, które mogą wymagać lepszego znacznika czasu;

Przykładem jest tak prosta

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

gdzie dzielnik 10^3^6 lub 10 w zależności od wymaganej rozdzielczości.

15

używam te określa:

/** 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żytkowania (nawiasy, aby zapobiec redefiniuje):

TIMER_INIT 

{ 
    TIMER_START 
    Sleep(1000); 
    TIMER_STOP 
} 

{ 
    TIMER_START 
    Sleep(1234); 
    TIMER_STOP 
} 

Wyjście z przykładu wykorzystania:

1.00003 sec 
1.23407 sec