web-dev-qa-db-fra.com

Comment utiliser QueryPerformanceCounter?

J'ai récemment décidé que je devais passer de millisecondes à microsecondes pour ma classe Timer, et après quelques recherches, j'ai décidé que QueryPerformanceCounter était probablement mon meilleur pari. (L'avertissement sur Boost::Posix Indiquant que cela ne fonctionnerait peut-être pas sur l'API Win32 m'a un peu gêné). Cependant, je ne sais pas trop comment le mettre en œuvre.

Ce que je fais est d'appeler n'importe quelle fonction GetTicks() esque que j'utilise et de l'assigner à la variable startingTicks de Timer. Ensuite, pour trouver le temps écoulé, je soustrais simplement la valeur de retour de la fonction du startingTicks et, lorsque je réinitialise le chronomètre, je réappelle la fonction et lui assigne de startTicks. Malheureusement, d'après le code que j'ai vu, ce n'est pas aussi simple que d'appeler QueryPerformanceCounter(), et je ne suis pas sûr de ce que je suis censé passer comme argument.

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;
}

Ce programme devrait générer un nombre proche de 1000 (Windows Sleep n'est pas si précis, mais il devrait ressembler à 999).

La fonction StartCounter() enregistre le nombre de ticks du compteur de performance dans la variable CounterStart. La fonction GetCounter() renvoie le nombre de millisecondes écoulées depuis le dernier appel de StartCounter() en double. Si GetCounter() renvoie 0,001, il s'écoule environ 1 microseconde depuis StartCounter() a été appelé.

Si vous voulez que le minuteur utilise les secondes à la place, changez

PCFreq = double(li.QuadPart)/1000.0;

à

PCFreq = double(li.QuadPart);

ou si vous voulez microsecondes puis utilisez

PCFreq = double(li.QuadPart)/1000000.0;

Mais c’est vraiment une question de commodité, car cela donne un double.

153
Ramónster

J'utilise ces définit:

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

Utilisation (parenthèses pour éviter les redéfinitions):

TIMER_INIT

{
   TIMER_START
   Sleep(1000);
   TIMER_STOP
}

{
   TIMER_START
   Sleep(1234);
   TIMER_STOP
}

Sortie de l'exemple d'utilisation:

1.00003 sec
1.23407 sec
17
user152949

En supposant que vous soyez sur Windows (si donc vous devriez taguer votre question comme telle!), Sur cette page MSDN , vous pouvez trouver le source d'une classe HRTimer simple et utile, qui encapsule les appels système nécessaires pour faire quelque chose de très proche de ce dont vous avez besoin (il serait facile d’y ajouter une méthode GetTicks()], en particulier de faire exactement ce dont vous avez besoin).

Sur les plateformes autres que Windows, il n'y a pas de fonction QueryPerformanceCounter, la solution ne sera donc pas directement portable. Cependant, si vous le placez dans une classe telle que le HRTimer mentionné ci-dessus, il sera plus facile de changer l'implémentation de la classe pour utiliser ce que la plate-forme actuelle est en mesure d'offrir (peut-être via Boost ou autre chose!). ).

2
Alex Martelli

Je voudrais prolonger cette question avec un exemple de pilote NDIS sur obtenir du temps. Comme on le sait, KeQuerySystemTime (imité sous NdisGetCurrentSystemTime) a une résolution faible supérieure à la milliseconde et certains processus, tels que les paquets réseau ou d’autres IRP, peuvent nécessiter un meilleur horodatage;

L'exemple est aussi simple:

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

où diviseur est 10 ^ 3, ou 10 ^ 6 en fonction de la résolution requise.

1
kagali-san