Как использовать QueryPerformanceCounter?

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

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

Ответ 1

#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() в последний раз назывался двойным, поэтому если GetCounter() возвращает 0.001, то это было около 1 микросекунды, так как был вызван StartCounter().

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

PCFreq = double(li.QuadPart)/1000.0;

к

PCFreq = double(li.QuadPart);

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

PCFreq = double(li.QuadPart)/1000000.0;

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

Ответ 2

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

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

Ответ 3

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

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

Ответ 4

Я бы распространил этот вопрос с примера драйвера 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 в зависимости от требуемого разрешения.