E D R , A S I H C RSS

Performance Test

프로그램의 성능을 측정는 방법. 프로그램이나 알고리즘의 성능을 알아보는 방법중 나가 수행시간 측정입니다.

HighResolutionTimer 사용

Windows 에서의 수행시간측정 방법.
~cpp 
 BOOL QueryPerformanceFrequency(LARGE_INTEGER* param)
 BOOL QueryPerformanceCounter(LARGE_INTEGER* param)
상기 두 Windows API함수를 사용해서 수행 시간을 측정 할 수 있습니다.

예제

수행시간 측정용 C++ Class. 수행시간 단위는 Sec 입니다. 단 HighResolutionTimer를 지원는 프로세서가 필요합니다.
다음은 Binary Search 의 퍼포먼스 측정관련 예제. CTimeEstimate 클래스를 만들어 씁니다.
~cpp 
 #include <windows.h>
 #include <time.h>
 #include <stdio.h>

 class CTimeEstimate
 {
 protected:
	__int64 m_nStart, m_nEnd, m_nFreq;

 public:
 	CTimeEstimate () {
                  m_nStart = m_nEnd = m_nFreq = 0;
                  QueryPerformanceFrequency((LARGE_INTEGER*)&m_nFreq);
         }

 	~CTimeEstimate () {   }

 	void Start () {
 		QueryPerformanceCounter((LARGE_INTEGER*)&m_nStart);
 	}

 	void End () {
 		QueryPerformanceCounter((LARGE_INTEGER*)&m_nEnd);
 	}

 	double Result () {
 		return (double)(m_nEnd - m_nStart)/m_nFreq;
 	}
 };

 void Init (int S[]);
 int getRandNum (int nBoundary);
 int BinarySearch (int nBoundary, int S[], int nKey);

 int main (void)
 {
 	CTimeEstimate est;

 	int S[30001];
 	int i, nRandNum, nLocation;
 	int nBoundary = 2000;

 	Init (S);
 	nRandNum = getRandNum (nBoundary);

 	est.Start ();
 	for (i=0;i<1000;i++) {
 		nLocation = BinarySearch (nBoundary, S, nRandNum);
 	}
 	est.End ();

 	// result
 	printf ("random number : %d \n", nRandNum);
 	printf ("location : %d\n", nLocation);
 	printf ("estimated time : %f \n", est.Result ());

 	return 0;
 }

 void Init (int S[]) {
 	for (int i=1;i<30001;i++) {
 		S[i] = i;
 	}
 }

 int getRandNum (int nBoundary) {
 	time_t t;

 	t = time (NULL);
 	srand (t);

 	return rand () % nBoundary;
 }

 int BinarySearch (int nBoundary, int S[], int nKey)
 {
 	int nBoundaryLow, nBoundaryHigh, nMiddleKey;
 	nBoundaryLow = 1; nBoundaryHigh = nBoundary;

 	while ((nBoundaryLow <= nBoundaryHigh) && nMiddleKey) {
 		nMiddleKey = (nBoundaryLow + nBoundaryHigh) / 2;

 		if (nKey == S[nMiddleKey])
 			return nMiddleKey;
 		else if (nKey < S[nMiddleKey])
 			nBoundaryHigh = nMiddleKey - 1;
 		else
 			nBoundaryLow = nMiddleKey + 1;
 	}

 	return FALSE;
 }

ftime함수, timeb 구조체의 사용

비교적 CPU와 OS에 의존적이지 않은 방법으로는 ftime 함수와 timeb 구조체를 사용는 방법이 있습니다. 밀리세컨드 단위까지 밖에 제공되지 않습니다. sys/timeb.h 헤더에 정의된 내용이 ANSI C 는 아니라고 알고있습니다.

ftime 함수의 프로토타입

~cpp 
 void ftime(struct timeb* buf)

timeb 구조체

~cpp 
 struct timeb {
  long time ;      /* seconds since 00:00:00, 1/1/70, GMT */
  short millitm ;  /* fraction of second  (in milliseconds) */
  short timezone ; /* difference between local time and GMT */
  short dstflag ;  /* 0 if daylight savings time is not in effect */
 };

ftime 함수와 timeb 구조체 사용 예

~cpp 
 #include <sys/timeb.h>
 #include <stdio.h>

 void show_est(struct timeb start, struct timeb end);

 int main(void)
 {
    struct timeb start, end;
    ftime(&start);
    sleep(1);
    ftime(&end);
    show_est(start,end);
 }

 void show_est(struct timeb start, struct timeb end)
 {
    int time, millitm;
    time = (int)(end.time - start.time);
    millitm = (int)(end.millitm - start.millitm);
    if (millitm<0)
    {
       time --;
       millitm += 1000;
    }
    printf (" %d ms 걸렸습니다.\n",time*1000+millitm);
 }

RDTSC 의 사용

마이크로 소프트웨어 1999년 2월호 테크니컬 컬럼에 나온 방법입니다.
펜티엄 이상의 CPU에서 RDTSC(Read from Time Stamp Counter)를 이용는 방법이 있다. 펜티엄은 내부적으로 TSC(Time Stamp Counter)라는 64비트 카운터를 가지고 있는데 이 카운터의 값은 클럭 사이클마다 증가한다. RDTSC는 내부 TSC카운터의 값을 EDX와 EAX 레지스터에 복사는 명령이다. 이 명령은 6에서 11클럭을 소요한다. Win32 API의 QueryPerformanceCounter도 이 명령을 이용해 구현한 것으로 추측된다. 인라인 어셈블러를 사용여 다음과 같이 사용할 수 있다.
~cpp 
 #define rdtsc(x) \
 { __asm __emit 0fh __asm __emit 031h __asm mov x, eax}
 #define rdtscEx(low, high) \
 { __asm __emit 0fh __asm __emit 031h __asm mov low, eax __asm mov high, edx}
간단게 32비트 정수로 사용고자 한다면 RDTSC명령이 카운터에서 가져오는 값 중에서 EAX에 담긴 값만을 가져오는 방법이 있다. 짧은 시간동안 측정한다면 EAX에 담긴 값만 가지고도 클럭을 측정할 수 있다. 64비트를 모두 이용할려면 LARGE_INTEGER 구조체를 이용한다.
~cpp 
 LARGE_INTEGER start, end

 rdtscEx(start.LowPart, start.EndPart);
 ..... // 측정 구간
 rdtscEx(end.LowPart, start.EndPart);

 elasped_time = *(__int64*)&end - *(__int64*)&start;
rdtscEx명령은 36클럭을 소요며 측정 구간을 클럭 단위로 측정할 수 있는 강력한 시간 측정 방법이다. 지만 이 방법은 클럭 수만 측정할 뿐 시간을 알 수는 없다. 정확한 시간을 알려면 시스템의 CPU클럭을 알아야 며 측정한 클럭값을 CPU클럭으로 나누어야 시간이 나온다. RDTSC명령을 수행할 때 CPU가 수행 속도 향상을 위해서 CPU 명령 순서가 바뀔 수 있기 때문에 CPUID명령을 전에 수행해 명령 순서를 맞춰야 는 경우도 있다. 자세한 설명은 인텔에서 제공는 성능 모니터링을 위한 RDTSC 명령 사용법을 참조기 바란다.


Windows는 Multi-Thread로 동작지 않습니까? 위 코드를 수행다가 다른 Thread로 제어가 넘어가게 되면 어떻게 될까요? 아마 다른 Thread의 수행시간까지 덤으로 추가되지 않을까요? 따라서 위에서 작성신 코드들은 정확한 수행시간을 측정지 못 할 것 같습니다. 그렇다고 제가 정확한 수행시간 측정을 위한 코드 작성 방법을 알지는 못합니다. -_-;

단, 정확한 수행시간 측정을 위해서라면 전문 Profiling Tool을 이용해 보는 것은 어떨까요? NuMega DPS 같은 제품들은 수행시간 측정을 아주 편게 할 수 있고 측정 결과도 소스 코드 레벨까지 지원해 줍니다. 마소 부록 CD에서 평가판을 찾을 수 있습니다. 단, 사용실 때 Development Studio 가 조금 맛이 갈겁니다. 이거 나중에 NuMega DPS 지우시면 정상으로 돌아갑니다. 그럼 이만. -- '96 박성수

p.s. NuMega 제품들이 어떻게 수행시간 측정는지 아시는 분 글 좀 올려주시죠?

멀티쓰레드로 인해 제어권이 넘어가는 것까지 고려해야 한다면 차라리 도스 같은 싱글테스킹 OS에서 알고리즘 수행시간을 계산는게 낫지 않을까 는 생각도 해봅니다. (지만, 만일 TSR 프로그램 같은 것이 인터럽트 가로챈다면 역시 마찬가지 문제가 발생할듯..) 그리고 단순한 프로그램의 병목부분을 찾기 위한 수행시간 계산이라면 Visual C++ 에 있는 Profiler 를 사용는 방법도 괜찮을 것 같습니다. 해당 함수들의 수행시간들을 보여주니까요.

NuMaga DPS 면 Dev-Partner Studio 말씀인가 보죠? (전에 Bound Checker 소문만 들어봐서..~) --1002
Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:24:00
Processing time 0.0163 sec