2009-11-15 2 views
88

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

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

+2

Я взял фрагменты кода Ramonster и превратили их в библиотеку здесь: https://gist.github.com/1153062 для последователей. – rogerdpack

+3

Недавно мы обновили документацию для QueryPerformanceCounter и добавили дополнительную информацию для правильного использования и ответы на часто задаваемые вопросы. Вы можете найти обновленную документацию здесь http://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx –

+0

как раз упомянуть [__rdtsc] (https: // msdn.microsoft.com/en-us/library/twchhe95.aspx), это то, что использует QueryPerformanceCounter. –

ответ

144
#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 (Windows сна не так точно, но она должна быть 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; 

Но на самом деле это об удобстве так как он возвращает double.

+0

lol, я просто закодировал это несколько дней назад –

+2

Точно, что такое LARGE_INTEGER? – Anonymous

+3

Это тип окна, в основном портативное 64-битное целое число. Это определение зависит от того, поддерживает ли целевая система 64-битные целые числа или нет. Если система не поддерживает 64-битные int, тогда она определяется как 32-битные ints, HighPart и LowPart. Если система поддерживает 64-битные int, то это объединение между 32-битными int и 64-битным int, называемым QuadPart. –

2

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

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

1

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

15

Я использую эти устанавливает:

/** 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 
Смежные вопросы