이번에 알아보는 것은 Visual C++에서 시간을 측정하는 방법입니다. Visual C++에서 시간을 측정하는 방법은 여러 가지가 있는데 . ANSI C의 함수를 이용하는 방법부터 윈도우 API 함수를 이용하는 방법까지 다양합니다. 그리고, 각각의 방법을 사용하기 위해서 함수에 따라 적절히 헤더를 Include 해줘야 하기도 합니다.
다음은 DEVPIA와 MSDN 을 통해서 찾은 여러 가지 시간 측정하는 방법입니다. 여러 님들이 올리신 글들을 제가 한번 정리해 본 것이구요, 소스를 직접 테스트 해보기도 하고 수정을 하기도 했습니다. 그리고 각각의 방법들은 형태는 다르지만 모두 공통점이 있습니다. 바로 실행 시간을 측정하기 위해서 원하는 루틴의 시작점과 종료점에서 시간 측정과 관련하는 함수를 부른다는 겁니다. 그래서 그 루틴이 시작할 당시에 비해 CPU 클럭이 얼마나 지났는지를 따져서, 즉 그 시작점과 종료점의 클럭의 차이를 이용해서 실행 시간을 구합니다.
간단한 설명은 여기서 마치고, 직접 한번 살펴보세요. 그리고, 직접 한 번 돌려보시길 바랍니다.
눈으로만 읽어선 절대로 자기의 것이 될 수 없으니까요..^^;
순서..
1. clock() 함수 이용 실행시간 측정.
2. timeGetTime()함수 이용 실행시간 측정.
3. QueryPerformanceCounter()함수 이용 실행시간 측정.
4. 매크로를 이용한 실행시간 측정. [밀리세컨트(ms)단위]
5. Debugging 중에 @CLK를 이용한 실행시간 측정.
6. 프로파일 기능을 이용한 각 함수들의 실행시간 알아보기.
]--------------------------------------------------------------------
1. clock() 함수 이용 실행시간 측정. [초(s)단위]
--------------------------------------------------------------------
어떤 작업을 수행하는데 걸리는 시간을 알고 싶은 경우는 흔하지만, 방법을 잘 몰라서 그냥 넘어가는 경우가 많습니다. 아래 소개된 방법이 가장 일반적인 방법이며 이보다 나은 방법은 아마 없다고 생각합니다. 그다지 어렵지 않으니 천천히 보세요.
컴퓨터에는 Clock Ticks라는 것이 있습니다. 그리고 운영체제에는 어떤 프로세스가 시작한지 얼마나 지났는지 알려주는 clock() 함수를 가지고 있구요. 이것을 이용하는 방법입니다.
clock_t current_tick = clock();
이 코드로 현재 프로세스가 실행한지 얼마나 지났는지를 Clock Tick단위로 알 수 있습니다. 시간을 알고 싶으시면 이 Clock Tick단위를 초단위로 변환해주면 되죠.
double current_sec = current_tick / CLOCKS_PER_SEC;
이 코드가 Tick 단위를 초단위로 환산해주는 코드입니다.
CLOCKS_PER_SEC은 time.h화일에 정의되어 있는 상수입니다.
간단히 예제를 만들어 보면,
#include "time.h"
void main()
{
clock_t before;
double result;
before = clock();
for ( int i = 0; i <32765; i++ ) printf("%dn", i );
result = (double)(clock() - before) / CLOCKS_PER_SEC;
printf("걸린시간은 %5.2f 입니다.n", result);
}
간단하게 C 스타일로 예를 들었지만, MFC환경에서도 잘 돌아갑니다.
--김현승(Puzzle)님이 올려주신 글입니다.
--------------------------------------------------------------------
2. timeGetTime() 함수 이용. [밀리세컨드(ms)단위]
--------------------------------------------------------------------
제가 즐겨 쓰는 방법은 multimedia timer를 사용하는 겁니다.
함수는 timeGetTime() 이구요, 밀리세컨드(ms)단위로 측정가능합니다.
방법은 아주 간단합니다.
DWORD dwStartTime = timeGetTime();
// 처리부분
...
// 처리부분 종료
DWORD dwEndTime = timeGetTime();
printf("%d ms", dwEndTime-dwStartTime);
필요한 부분에 추가하시고 쓰면 됩니다.
단, 사용하기 위해서는 사용하는 파일에 Mmsystem.h 를 Include 해주고,winmm.lib 를 Project=>Setting(ALT+F7) 메뉴의 LINK 텝에서 Object/library modules: 에 추가를 해주어야 합니다..
--DEVPIA에 올리신 김영훈(zenk)님의 글입니다.
-----------------------------------------------------------------------------
3. QueryPerformanceCounter() 함수 이용 [ ms단위]
-----------------------------------------------------------------------------
다음은 QueryPerformanceFrequency() 와 QueryPerformanceCounter()를 이용한 방법입니다. 밀리 세컨드 단위로 측정 가능하구요, #include <Windows.h> 를 해주어야 합니다. 한가지 단점은 시스템에 따라 얼마나 작은 시간을 잴 수 있는지가 제한된다는 것입니다. 하지만 CPU의 속도에 의해 결정되는 것은 아니므로 조금 느린 시스템이라고 해서 걱정하실 필요는 없습니다. API 의 도움말을 보면 시스템에 따라 CPU 클럭의 레벨까지도 체크가 가능하다는군요. 사용하는 API 함수는 High Performance Timer Functions라고 불리는 QueryPerformanceFrequency() 와 QueryPerformanceCounter() 라는 두 개의 함수입니다.
앞의 놈은 자기 시스템이 최대 어느 정도까지의 timer resolution을 지원하는지를 판별하는데 쓰이구요, 뒤엣 놈은 현재의 카운터를 알아내는 데 사용됩니다. 예를 들어 앞의 함수를 콜한 후 넘겨준 파라미터 값이 10000 으로 되어 있다면 그 시스템은 10000분의 1초 (=> 1/10000) 즉 0.1밀리초까지 판별할 수 있습니다.
그리고 어떤 작업을 수행하기전에 QueryPerformanceCounter를 한번실행하고 수행후 다시 콜함으로써 각각 얻어진 카운터값의 차이를 구함으로써 수행시간을 판단할 수 있습니다
LARGE_INTEGER Frequency;
LARGE_INTEGER BeginTime;
LARGE_INTEGER Endtime;
//프로그램이나 클래스 시작부분에
QueryPerformanceFrequency( &Frequency );
//사용하고자 하는 부분에 다음 코딩
QueryPerformanceCounter( &BeginTime );
처리함수();
QueryPerformanceCounter( &Endtime );
INT64 elapsed = Endtime.QuadPart- BeginTime.QuadPart;
double duringtime = (double)elapsed / (double)Frequency.QuadPart;
//다음은 변수를 달리 했을 때의 사용법입니다.
__int64 Frequency;
__int64 BeginTime;
__int64 Endtime;
//프로그램이나 클래스 시작부분에
QueryPerformanceFrequency((LARGE_INTEGER *) &Frequency );
//사용하고자 하는 부분에 다음 코딩
QueryPerformanceCounter((LARGE_INTEGER *) &BeginTime );
처리함수();
QueryPerformanceCounter((LARGE_INTEGER *) &endtime );
int64 elapsed = Endtime - BeginTime;
double duringtime = (double)elapsed / (double)Frequency.QuadPart;
--DEVPIA에 올리신 오희백(ohpwin)님의 글을 수정한 것입니다.
-------------------------------------------------------------------------
4. 매크로를 이용한 실행시간 측정. [밀리세컨드(ms)단위]
-------------------------------------------------------------------------
//다음 두줄을 복사해서 쓰시면 됩니다.
// 이 매크로는 Win32 함수를 이용하여 nano second까지 측정할 수 있습니다.
#define CHECK_TIME_START __int64 freq, start, end; BOOL condition; if (condition = QueryPerformanceFrequency((_LARGE_INTEGER*)&freq)) QueryPerformanceCounter((_LARGE_INTEGER*)&start);
// a는 float type milli second이고 b가 FALSE일때는 에러입니다
#define CHECK_TIME_END(a,b) if (condition) {QueryPerformanceCounter((_LARGE_INTEGER*)&end); a=(float)((double)(end - start)/freq*1000); b=TRUE;} else b=FALSE;
위의 내용을 조금 바꾸면 나노까지 측정 가능하죠.. 우선은 mili가지 측정됩니다.
그리고 windows.h포함해야 합니다. 매크로로 만들어 놔서 조금은 편하답니다.
사용법
CHECK_TIME_START;
... 실행시간 측정을 원하는 코드 ...
CHECK_TIME_END(time, err);
(첫 번째 인자는 float, 두 번째 인자는 BOOL)
--DEVPIA에 올리신 김태연(MonoEye)님의 글을 수정한 것입니다.
--------------------------------------------------------------------
5. Debugging 중에 @CLK를 이용한 실행시간 측정.
--------------------------------------------------------------------
다음은 실행시간을 측정하는 간단한 Debugging Technic 입니다.
프로그램을 하다 보면, 특정한 루틴이 과연 얼마나 많은 시간을 차지하는지 알고 싶은 경우가 있습니다. 물론, 많은 경우는 아니지만, 특정한 알고리즘을 만든 경우.. 다른 알고리즘과 처리 시간을 비교해 보고 싶은 경우가 있죠. 이럴 때 시스템 시간을 구해서 비교하는 방법들을 많이 사용하는데요,
실제로는 그렇게 할 필요가 없죠.. Watch Window 에서 앞에 골뱅이(@) 붙여서 사용하는 레지스트리 값들있죠? 골뱅이 시리즈 중에 하나를 사용하시면 된답니다.
@CLK
(CLK == Clock)
위에 있는 @CLK는 말이죠. 현재 프로세스의 Clocking을 나타내줍니다. 그런데 위의 값을 사용자가 임의로 초기화 할 수 있기 때문에, 유용하게 사용할 수가 있는 것이랍니다.
자.. 예를 들어서..
A 라는 루틴이 있는데, A 가 과연 얼마나 시간이 걸리는지 알고 싶습니다.
그러면 어떻게 @CLK를 사용해야 할까요?
우선 A 에 들어가기 전에 Break Pointer를 걸어두고, @CLK를 초기화 하시면 됩니다.
그리고 A 가 끝나는 부분에 다시 Break Pointer를 걸어두시면 바뀐 @CLK 값을 확인해 보실 수 있겠죠. @CLK 값은 밀리세컨드 단위니까. @CLK/1000,d 라고 하시면 초 단위로 보실 수 있습니다.
그럼 간단한 예제를 통해서 실제 사용방법을 살펴 보고 , 마무리 하겠습니다.
int main(int argc, char* argv[])
{
// Break Pointer 설치하는 방법
// Alt+F9 를 눌러서 Break at 란에.. 아래와 같이..
// {,"ClockTest.cpp",} .23
// {,"ClockTest.cpp",} .39
// 입력합니다.
// 첫번째 Break pointer
// 이 부분에서 'User Breakpointer' 가 call 되면
// Watch Window 에 아래의 값을 넣습니다.
// ----
// @CLK
// @CLK=0
// ----
// 위에서 @CLK 는 시스템 Clocking 을 사용하겠다는 말이고,
// @CLK=0 는 스스템 Clocking 을 0으로 초기화 하겠다는 말입니다.
// 이제 이 상태에서 다시 Go 버튼을 눌러서 프로그램을 실행하면..
// 두번째 User Breakpointer 가 call 됩니다.
int i, j, k;
for (i = 0 ; i <1000 ; i++)
for (j = 0 ; j <1000 ; j++)
for (k = 0 ; k <1000 ; k++)
;
// 두번째 Break Pointer
// 이 부분에서 'User Breakpointer' 가 call 되면 Watch Window 에 있는
// 첫번째 @CLK 값이 초과된 시간을 보여줍니다..
// 물론 이 값에는 Debugging 때문에 여러가지 추가 작업들이
// 행해져서 정확한 프로그램 실행시간이 아니지만, 여러가지
//작업을 상대적으로 비교할 수는 있겠죠..
return 0;
}
--DEVPIA에 올리신 서우석(seaousak)님의 글입니다.
------------------------------------------------------------------------------
6. 프로파일 기능을 이용한 각 함수들의 실행시간 알아보기
------------------------------------------------------------------------------
프로그램을 짜면서 각 함수가 어느 정도의 시간이 걸리는 지 알아보려는 노력들을 많이 합니다. 실제 실행시간일 수도 있고 전체 프로그램에서 어느 정도의 비율로 작용하는 지 알고싶을 때도 있구요...
이 때 프로그램의 각 함수별로 실행시간과 전체에서의 비율, 호출 횟수등을 알 수 있는 방법이 있습니다. 이것은 Debug모드에서만 가능합니다…
- Project – Settings 메뉴를 선택한다
- Link 탭으로 간다. ‘Enable profiling’을 선택한다.
- 그러고나면 Build 메뉴의 ‘Profile…’ 메뉴가 활성화가 된다.
- 반드시 Rebuild 시킨후에 ‘Profile…’ 메뉴를 선택한다. 만일 그렇지 않으면 다음과 같은 메시지박스가 뜬다
- Rebuild 시킨후 ‘Profile…’ 메뉴를 선택하면 다음과 같은 다이알로그가 뜬다
- 여기서 ‘Function timing’을 선택하고 ‘OK’를 누르면 프로그램이 실행이 될 것이다.
- 실행이 끝나고 나면 Output 창에 결과를 보여줄 것이다. 함수별 실행시간과 불려진 횟수 등을 확인할 수 있다.
이것을 이용하면 프로그램을 짠 후 오랜 시간이 걸리거나 짧으면서도 많이 호출되는 함수를 찾아서 프로그램을 개선시킬 수가 있다. 단, 실행되는 것이 디버그모드이고 이 때는 일반 디버그모드보다 시간이 더 걸린다는 것을 명심해야 합니다. 이 외에도 프로파일에는 여러 가지 기능이 있으니 사용해 보시기 바랍니다.
-DEVPIA에 올리신 이광진(mirjini)님의 글입니다