본문 바로가기

C++ 실전 강좌

[C++] 타이머 만들기 (QueryPerformanceCounter,GetTickCount)


보통 프로그램내에서 시간을 재고 싶을 때에는, 시작시의 값을 저장하고, 끝난 시점에서의 값을 알아낸 후, 두 값을 차이를 이용하면 됩니다.

이렇게 현재 시간을 알려 주는 시간 관련 함수는 다음과 같습니다.

1. time , _time64

time_t time( time_t *timer );
__time32_t _time32( __time32_t *timer );
__time64_t _time64( __time64_t *timer );

1970년 1월 1일 기준으로, 현재까지의 지난 시간을 초 단위로 알려 줍니다.
1970년도 부터 지나간 초를 반환하므로,32 비트 버전의 경우 32bit 이상값은 반환할 수 없으므로, 2038년 1월 18일이면 문제가 발생합니다.

이런 이유로 Visual C++ 2005 부터는 기본적으로 time_t 가 _time64 로 대체 되어 64비트로 동작하게 됩니다.
하지만 기존에 time_t 를 32비트 int 형태로 가정하고 프로그래밍한 경우에는, 소스를 찾아서 이를 모두 64비트에 맞게 수정해 주거나, _USE_32BIT_TIME_T 를 정의하여 기존 처럼 32비트 함수를 사용할 수도 있습니다.
(물론 지금 개발하는 프로그램이 2038년 까지 사용된다는 보장은 없으므로, 32비트로 사용해도 문제가 발생할 확률은 거의 없다고 생각할 수 있으나, 모든 프로젝트에 _USE_32BIT_TIME_T 를 정의해서 사용하는 것이 번거로운 작업이고, 64비트 환경을 미리 준비한다는 생각으로 기존 32비트 time 코드를 64비트로 수정하는 것이 바람직 합니다.)


2. timeGetTime()

DWORD timeGetTime(void);

밀리세컨드(millisecond) 단위로 현재 시스템 시간을 알려 줍니다.
DWORD 값이므로, 0 에서 2^32 밀리세컨드(약 49.71 일) 사이의 값을 반환합니다. 따라서 시간을 재려고 하는 간격이 49일 이상인 경우는 이 값을 직접사용해서는 안되고, 일정 간격으로 timeGetTime() 호출하여, 각 함수 호출간에 지난 시간을 재고, 이 값들을 더해서 사용하는 방법을 사용해야 합니다.
기본 정밀도는 컴퓨터에 따라 다르지만 약 5 밀리세컨드 정도입니다. timeBeginPeroid 와 timeEndPeriod 를 이용해 정밀도 값을 늘려 줄수 있습니다.


3. GetTickCount()

DWORD WINAPI GetTickCount(void);

시스템이 시작된 이후 지난 시간을 밀리세컨드(millisecond) 단위로 알려 줍니다.
GetTickCount() 함수의 정밀도는 시스템 타이머의 정밀도에 의존합니다. 보통 10 에서 16 밀리세컨드 정도입니다.
반환값이 DWORD 이므로, 당연히 약 49,7일이 지나면 0부터 다시 시작되게 됩니다.
GetTickCount64() 함수를 사용하면 이런 문제를 해결할 수 있습니다.

보다 정밀한 타이머를 원한다면 멀티미디어 타이머(Multimedia Timer) 또는 고성능 타이머(high-resolution timer) 를 사용해야 합니다.


4. 멀티미디어 타이머(Multimedia Timers)

MMRESULT timeSetEvent(UINT uDelay,UINT uResolution,LPTIMECALLBACK lpTimeProc,DWORD_PTR dwUser,UINT fuEvent);
MMRESULT timeKillEvent(UINT uTimerID);


현재 시간을 알아 내는 것이 아니라, 지정된 시간이 지나면 콜백 함수가 호출되도록 합니다.
보통은 스케줄러등에 사용할 수 있습니다.


5. 고성능 타이머(High-Resolution Timer)

BOOL WINAPI QueryPerformanceCounter( __out  LARGE_INTEGER *lpPerformanceCount);
BOOL WINAPI QueryPerformanceFrequency( __out  LARGE_INTEGER *lpFrequency);


QueryPerformanceCounter() 함수는 현재 퍼포먼스 카운터(Performance Counter) 값을 알려 줍니다.
시작시의 카운터 값과 종료시의 카운터 값의 차이를 구한 후, QueryPerformanceFrequency에서 알려 주는 타이머 주기(Frequence) 값으로 나누면 경과 시간을 알 수 있습니다.
하드웨어 기반의 초정밀 타이머를 만들 수 있습니다.

멀티프로세서(Multiprocessor) 컴퓨터인 경우,시스템 BIOS 또는 HAL(Hardware Abstraction Layer)의 버그로 인해 각 프로세서 마다 다른 값을 반환합니다. 따라서 어떤 프로세서를 사용할 지를 미리 지정하지 않으면 문제가 발생할 수 있습니다.
예를 들어 시작 시점에 QueryPerformaceCounter()를 호출할 때는 0번 프로세서의 값이 반환되고, 종료 시점에 호출 할 때는 1번 프로세서 값이 반환될 수도 있다는 뜻입니다. 이럴 경우 정확한 시간 계산이 되지 않습니다.
이를 방지하기 위해 SetThreadAffinityMask 함수를 사용해 원하는 프로세서를 지정해야 합니다.


참고
http://www.codeproject.com/Articles/175033/An-eternal-question-of-timing

'C++ 실전 강좌' 카테고리의 다른 글

[C++] Volatile 키워드  (2) 2011.06.10