QueryPerformanceCounter를 사용하는 방법?


97

최근에 Timer 클래스를 밀리 초에서 마이크로 초로 변경해야한다고 결정했고, 몇 가지 조사 끝에 QueryPerformanceCounter가 아마도 가장 안전한 방법이라고 결정했습니다. ( Boost::PosixWin32 API에서 작동하지 않을 수 있다는 경고 는 나를 약간 실망 시켰습니다.) 그러나 구현 방법을 잘 모르겠습니다.

내가하는 일은 GetTicks()내가 사용 하는 esque 함수를 호출 하고 Timer의 startingTicks변수에 할당하는 것 입니다. 그런 다음 경과 된 시간을 확인하기 위해에서 함수의 반환 값을 빼고 startingTicks타이머를 재설정 할 때 함수를 다시 호출하고 startingTicks를 할당합니다. 불행히도 코드에서를 호출하는 것만 큼 간단 QueryPerformanceCounter()하지 않으며 인수로 전달해야 할 내용이 확실하지 않습니다.


2
Ramonster의 코드 조각을 가져와 여기에있는 라이브러리로 만들었습니다 : gist.github.com/1153062 for followers.
rogerdpack

3
최근에 QueryPerformanceCounter에 대한 설명서를 업데이트하고 적절한 사용법과 FAQ에 대한 답변을 추가했습니다. 업데이트 된 설명서는 여기에서 찾을 수 있습니다. msdn.microsoft.com/en-us/library/windows/desktop/…
Ed Briggs

__rdtsc 를 언급하는 것과 마찬가지로 QueryPerformanceCounter가 사용하는 것입니다.
colin lamarre

답변:


159
#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;

그러나 실제로는 double을 반환하기 때문에 편리합니다.


5
정확히 LARGE_INTEGER는 무엇입니까?
Anonymous

5
기본적으로 이식 가능한 64 비트 정수인 Windows 유형입니다. 정의는 대상 시스템이 64 비트 정수를 지원하는지 여부에 따라 다릅니다. 시스템이 64 비트 정수를 지원하지 않는 경우 2 개의 32 비트 정수, HighPart 및 LowPart로 정의됩니다. 시스템이 64 비트 정수를 지원하는 경우 2 개의 32 비트 정수와 쿼드 파트라고하는 64 비트 정수 간의 결합입니다.
Ramónster

8
이 대답에는 심각한 결함이 있습니다. QueryPerformanceCounter는 코어 특정 사이클 카운터 레지스터를 읽고, 실행 스레드가 다른 코어에서 다시 예약 된 경우 QueryPerformanceCounter의 두 측정은 경과 시간뿐만 아니라 두 코어 레지스터 사이의 고정 된 크고 정확한 델타를 통합하는 경우가 많습니다. 따라서 이것은 프로세스가 특정 코어에 바인딩 된 경우에만 안정적으로 작동합니다.
Tony Delroy

15
@TonyD : MSDN 문서에 따르면 : On a multiprocessor computer, it should not matter which processor is called. However, you can get different results on different processors due to bugs in the basic input/output system (BIOS) or the hardware abstraction layer (HAL).이 코드에는 심각한 결함이 없지만 일부 BIOS 또는 HAL이 있습니다.
Lucas

3
@TonyD : 나는 이것을 조금 더 조사했습니다. StartCounter함수에 다음 호출을 추가 한 다음 old_mask = SetThreadAffinityMask(GetCurrentThread,1);끝에 다시 설정합니다 SetThreadAffinityMask ( GetCurrentThread , old_mask ) ;. 나는 그것이 트릭을 할 수 있기를 바랍니다. 이것은 내 스레드가 첫 번째 CPU 코어 이외의 다른 것으로 다시 예약되는 것을 방지합니다. (분명히 테스트 환경을위한 솔루션 일뿐입니다.)
Lucas

19

이 정의를 사용합니다.

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

Windows (질문에 태그를 지정해야하는 경우)를 사용한다고 가정하면 이 MSDN 페이지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입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.