Как использовать QueryPerformanceCounter? - PullRequest
92 голосов
/ 16 ноября 2009

Недавно я решил, что мне нужно перейти от использования миллисекунд к микросекундам для своего класса Timer, и после некоторых исследований я решил, что QueryPerformanceCounter, вероятно, является моей самой безопасной ставкой. (Предупреждение на Boost::Posix о том, что оно может не работать на Win32 API, немного оттолкнуло меня). Однако я не совсем уверен, как это реализовать.

То, что я делаю, - это вызов любой функции GetTicks() esque, которую я использую, и присвоение ее переменной startingTicks таймера. Затем, чтобы найти количество прошедшего времени, я просто вычитаю возвращаемое значение функции из startingTicks, а когда я сбрасываю таймер, я просто вызываю функцию снова и присваиваю ей startTicks. К сожалению, из кода, который я видел, это не так просто, как просто вызвать QueryPerformanceCounter(), и я не уверен, что я должен передать в качестве аргумента.

Ответы [ 4 ]

152 голосов
/ 16 ноября 2009
#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;
}

Эта программа должна выводить число, близкое к 1000 (режим сна не такой точный, но он должен быть как 999).

Функция StartCounter() записывает количество тактов, которое счетчик производительности имеет в переменной CounterStart. Функция GetCounter() возвращает количество миллисекунд с тех пор, как StartCounter() последний раз вызывался как double, поэтому, если GetCounter() возвращает 0,001, то с момента вызова StartCounter() прошло около 1 мкс.

Если вы хотите, чтобы таймер использовал секунды вместо этого, измените

PCFreq = double(li.QuadPart)/1000.0;

до

PCFreq = double(li.QuadPart);

или, если вы хотите микросекунды, используйте

PCFreq = double(li.QuadPart)/1000000.0;

Но на самом деле речь идет об удобстве, поскольку возвращает двойное число.

17 голосов
/ 10 апреля 2013

Я использую эти определения:

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

Использование (скобки для предотвращения переопределения):

TIMER_INIT

{
   TIMER_START
   Sleep(1000);
   TIMER_STOP
}

{
   TIMER_START
   Sleep(1234);
   TIMER_STOP
}

Вывод из примера использования:

1.00003 sec
1.23407 sec
2 голосов
/ 16 ноября 2009

Предполагая, что вы находитесь в Windows (если это так, вы должны пометить свой вопрос как таковой!), На этой странице MSDN вы можете найти источник для простого, полезного HRTimer C ++ класса, который переносит системные вызовы, чтобы сделать что-то очень близкое к тому, что вам нужно (было бы легко добавить к нему метод GetTicks(), в частности, сделать точно то, что вам требуется).

На платформах, отличных от Windows, функция QueryPerformanceCounter отсутствует, поэтому решение не будет напрямую переносимым. Однако, если вы поместите его в класс, такой как вышеупомянутый HRTimer, будет легче изменить реализацию класса, чтобы использовать то, что действительно может предложить текущая платформа (возможно, через Boost или что-то еще!).

1 голос
/ 21 января 2012

Я бы расширил этот вопрос примером драйвера NDIS для получения времени. Как известно, KeQuerySystemTime (имитируется под NdisGetCurrentSystemTime) имеет низкое разрешение, превышающее миллисекунды, и существуют некоторые процессы, такие как сетевые пакеты или другие IRP, которые могут нуждаться в лучшей временной отметке;

Пример такой же простой:

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

, где делитель равен 10 ^ 3 или 10 ^ 6 в зависимости от требуемого разрешения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...