it-swarm.com.de

Wie wird QueryPerformanceCounter verwendet?

Ich habe kürzlich entschieden, dass ich für meine Timer-Klasse von Millisekunden auf Mikrosekunden umsteigen muss, und nach einigen Recherchen habe ich entschieden, dass QueryPerformanceCounter wahrscheinlich meine sicherste Wette ist. (Die Warnung auf Boost::Posix, Dass es auf der Win32-API möglicherweise nicht funktioniert, hat mich ein wenig abgeschreckt). Ich bin mir jedoch nicht sicher, wie ich es implementieren soll.

Ich rufe die von mir verwendete Funktion GetTicks() esque auf und weise sie der Variablen startingTicks des Timers zu. Um die verstrichene Zeit zu ermitteln, subtrahiere ich einfach den Rückgabewert der Funktion von startingTicks. Wenn ich den Timer zurücksetze, rufe ich die Funktion einfach erneut auf und weise ihr Startticks zu. Leider ist es aus dem Code, den ich gesehen habe, nicht so einfach, QueryPerformanceCounter() aufzurufen, und ich bin mir nicht sicher, was ich als Argument übergeben soll.

92
Anonymous
#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;
}

Dieses Programm sollte eine Zahl in der Nähe von 1000 ausgeben (Windows Sleep ist nicht so genau, sollte aber 999 sein).

Die Funktion StartCounter() zeichnet die Anzahl der Ticks auf, die der Leistungsindikator in der Variablen CounterStart hat. Die Funktion GetCounter() gibt die Anzahl der Millisekunden seit dem letzten Aufruf von StartCounter() als double zurück. Wenn also GetCounter() 0,001 zurückgibt, sind es ungefähr 1 Mikrosekunde seit StartCounter() wurde aufgerufen.

Wenn Sie möchten, dass der Timer stattdessen Sekunden verwendet, ändern Sie ihn

PCFreq = double(li.QuadPart)/1000.0;

zu

PCFreq = double(li.QuadPart);

oder wenn Sie Mikrosekunden wollen, dann verwenden Sie

PCFreq = double(li.QuadPart)/1000000.0;

Aber es geht wirklich um Bequemlichkeit, denn es gibt ein Doppel zurück.

153
Ramónster

Ich benutze diese Definitionen:

/** 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;

Verwendung (Klammern, um Neudefinitionen zu verhindern):

TIMER_INIT

{
   TIMER_START
   Sleep(1000);
   TIMER_STOP
}

{
   TIMER_START
   Sleep(1234);
   TIMER_STOP
}

Ausgabe aus Verwendungsbeispiel:

1.00003 sec
1.23407 sec
17
user152949

Angenommen, Sie arbeiten unter Windows (wenn ja, sollten Sie Ihre Frage als solche kennzeichnen!), Dann können Sie auf dieser MSDN-Seite die Quelle für eine einfache, nützliche HRTimer C++ - Klasse finden, die umbrochen wird die erforderlichen Systemaufrufe, um etwas zu tun, das Ihren Anforderungen sehr nahe kommt (es wäre einfach, eine GetTicks() -Methode hinzuzufügen, insbesondere, um genau zu tun was Sie benötigen).

Auf Nicht-Windows-Plattformen gibt es keine QueryPerformanceCounter-Funktion, sodass die Lösung nicht direkt portierbar ist. Wenn Sie es jedoch in eine Klasse wie die oben erwähnte HRTimer einbinden, ist es einfacher, die Implementierung der Klasse zu ändern, um das zu verwenden, was die aktuelle Plattform tatsächlich bieten kann (möglicherweise über Boost oder was auch immer)! ).

2
Alex Martelli

Ich würde diese Frage mit einem NDIS-Treiberbeispiel zum Thema Zeit erweitern. Wie man weiß, hat KeQuerySystemTime (imitiert unter NdisGetCurrentSystemTime) eine niedrige Auflösung über Millisekunden, und es gibt einige Prozesse wie Netzwerkpakete oder andere IRPs, die einen besseren Zeitstempel benötigen.

Das Beispiel ist genauso einfach:

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

wobei der Divisor 10 ^ 3 oder 10 ^ 6 ist, abhängig von der erforderlichen Auflösung.

1
kagali-san