Как использовать 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 в зависимости от требуемого разрешения.