기본 콘텐츠로 건너뛰기

윈도우 상에서 시간 측정하기

이 글은 마이크로소프트웨어 99 2월의 "이보다 더 정확할 순 없다! 윈도우 환경에서 시간측정하는법" 이란 기사에서 참고한 것입니다.

 

윈도우 상에서 시간 측정하기

 

 윈도우 상에서 시간을 측정하기 위한 방법에는 몇가지가 있는데, clock() 함수를 사용하거나 WM_TIMER 메시지를 사용한다거나, 일반 타이머보다 더 높은 정확도를 위해 멀티미디어 타이머를 사용할 수 있다.

clock() 함수를 사용하는 방법은 "C/C++ 관련 내용" "정렬 (Sort)" 부분에서 볼 수 있다.

WM_TIMER의 경우는 초당 클럭수가 18.3 이므로 1/18.3 , 55 ms 정도의 정확도를 가진다. 그리고 멀티미디어 타이머의 경우는 최소시간 간격이 1ms 이고, 10ms 이내의 이벤트 지연시간을 가지는 타이머 이벤트는 CPU 자원을 많이 소모하기 때문에 주의해야 한다.

앞서 설명한 멀티미디어 타이머는 최소 시간간격이 1ms, 그 이하의 시간을 측정하는데는 적합하지 않다. 더구나 CPU의 성능이 높아지면서 1ms는 무척 긴 시간이 돼 버렸다. 1ms 동안 많은 명령을 수행할 수 있기 때문에 네트웍 패킷의 전송시간이나 특정 루틴의 시간을 측정하기 위해서는 멀티미디어 타이머는 도움이 되지 않는다. 이러한 경우에 Win32 API에서 제공하는 QueryPerformanceFrequency QueryPerformanceCounter , 이 두 개의 함수가 유용하게 쓰인다.

 QueryPerformanceFrequency 1초 동안 카운터가 증가하는 값을 얻어내는데, 시스템이 이 두 함수들을 지원하지 않으면 QueryPerformanceFrequency 의 값이 0 이 되고 결과값도 0 이 돌아온다.

다음 코드는 VC++에서 카운터를 사용한 예이다.

//-----------------------------------------------------------

#include "stdafx.h"  // pre-compiled header file for VC++
#include "windows.h"
#include "stdio.h"

void main()
{
        LARGE_INTEGER freq, start, end;
        LARGE_INTEGER tmp;

        if(QueryPerformanceFrequency(&freq)) {
                QueryPerformanceCounter(&start);
                printf("Performance Frequency is %I64d\n", freq);
                QueryPerformanceCounter(&end);
                printf("Performance Resolution is %4.3f micro sec.\n",
                        1.0/freq.QuadPart * 1000000);
                printf("printf elapsed time is %4.3f micro sec.\n",
                        (double)(end.QuadPart-start.QuadPart)/freq.QuadPart*1000000);

                QueryPerformanceCounter(&start);
                        QueryPerformanceCounter(&tmp); // 1
                        QueryPerformanceCounter(&tmp); // 2
                        QueryPerformanceCounter(&tmp); // 3
                        QueryPerformanceCounter(&tmp); // 4
                        QueryPerformanceCounter(&tmp); // 5
                        QueryPerformanceCounter(&tmp); // 6
                        QueryPerformanceCounter(&tmp); // 7
                        QueryPerformanceCounter(&tmp); // 8
                        QueryPerformanceCounter(&tmp); // 9
                        QueryPerformanceCounter(&tmp); // 10
                QueryPerformanceCounter(&end);

                printf("QueryPerformance delay time is %4.3f micro sec.\n",
                        (double)(end.QuadPart-start.QuadPart)/freq.QuadPart*100000); //  /10
        } else
                printf("This computer don't support QueryPerformance function sets !!\n");
}

//--------------------------------------------------------

 Pentium II-266 , Windows 2000 professional 에서의 결과는 다음과 같다.

Performance Frequency is 3579545
Performance Resolution is 0.279 micro sec.
printf elapsed time is 801.499 micro sec.
QueryPerformance delay time is 3.771 micro sec.

 

위 코드에서 LARGE_INTEGER 에 대한 MSDN의 설명은 다음과 같다.

The LARGE_INTEGER structure is used to represent a 64-bit signed integer value.

typedef union _LARGE_INTEGER {

    struct {

        DWORD LowPart;

        LONG  HighPart;

    };

    LONGLONG QuadPart;

} LARGE_INTEGER;

 

Members

LowPart

Specifies the low-order 32 bits.

HighPart

Specifies the high-order 32 bits.

QuadPart
        Specifies a 64-bit signed integer.

====================================================================

앞에서 설명한 API 보다 좀 더 정확한 방법으로 수행 시간을 측정하기 바란다면 한 가지 방법이 있다. 이 방법은 Win32 API를 이용한 것이 아니라 어셈블리를 이용한 방법으로 인텔 펜티엄 이상에서만 수행시킬 수 있는 방법이다. 펜티엄에서 추가된 명령중에 RDTSC(Read from Time Stamp Counter) 라는 것이 있다. 펜티엄은 내부적으로 TSC(Time Stamp Counter)라는 64비트 카운터를 가지고 있는데, 이 카운터의 값은 클럭 사이클마다 증가한다. RDTSC 명령은 내부 TSC 카운터의 값을 EDX EAX 레지스터에 복사하는 명령이다. 이 명령은 6에서 11 클럭을 소요한다.

다음의 rdtscEx 명령은 36 클럭을 소요하며 측정구간을 클럭단위로 측정할 수 있는 강력한 시간 측정 방법이다. 하지만 이 방법은 클럭 수만 측정할 뿐 시간을 알 수는 없다. 정확한 시간을 알려면 시스템의 CPU 클럭을 알아야 하며 측정한 클럭값을 CPU 클럭으로 나누어야 시간이 나온다.

 RDTSC 명령을 수행할 때 CPU가 수행속도 향상을 위해서 CPU 명령순서가 바뀔 수 있기 때문에 CPUID 명령을 전에 수행해 명령순서를 맞춰야 하는 경우도 있다.

//------------------------------------------------------

#include "stdafx.h"  // pre-compiled header file for VC++
#include <windows.h>
#include <stdio.h>

#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}
#define cpuid {__asm __emit 0fh __asm __emit 0a2h}

int main(void)
{
    LARGE_INTEGER start, end, tmp;
    __int64 freq, diff;

    if (QueryPerformanceFrequency((LARGE_INTEGER*)&freq)) {
        rdtscEx(start.LowPart, start.HighPart);
        printf("Performance Frequency is %I64d\n", freq);
        rdtscEx(end.LowPart, end.HighPart);
        printf("Preformance Resolution is %4.3f micro sec.\n",
                        1.0/freq * 1000000);
        diff = *(__int64*)&end - *(__int64*)&start;
        printf("printf elapsed clock cycle is %I64d\n", diff);
        printf("printf elapsed time is %4.3f micro sec.\n",
                        (double)diff/266);  //
클럭에 따라 이 부분이 바뀌어야한다.

        rdtscEx(start.LowPart, start.HighPart);
                rdtscEx(tmp.LowPart, tmp.HighPart); // 1
                rdtscEx(tmp.LowPart, tmp.HighPart);
                rdtscEx(tmp.LowPart, tmp.HighPart);
                rdtscEx(tmp.LowPart, tmp.HighPart);
                rdtscEx(tmp.LowPart, tmp.HighPart);
                rdtscEx(tmp.LowPart, tmp.HighPart);
                rdtscEx(tmp.LowPart, tmp.HighPart);
                rdtscEx(tmp.LowPart, tmp.HighPart);
                rdtscEx(tmp.LowPart, tmp.HighPart);
                rdtscEx(tmp.LowPart, tmp.HighPart); // 10
        rdtscEx(end.LowPart, end.HighPart);
        printf("rdtscEx elapsed clock clycle is %I64d\n",
                        (*(__int64*)&end - *(__int64*)&start)/10);
    } else
        printf("This computer don't support QueryPerformance function sets!!\n");
}

//----------------------------------------------------

결과는 다음과 같다.

Performance Frequency is 3579545
Preformance Resolution is 0.279 micro sec.
printf elapsed clock cycle is 182771
printf elapsed time is 687.109 micro sec.
rdtscEx elapsed clock clycle is 36

 

지금까지 측정한 시간들은 절대적인 값이라 볼 수는 없다. 실제로 위에서 소개한 두 코드를 가지고 여러번 실행해 보면 그때마다 값이 조금씩 다르게 나오는 것을 볼 수 있는데 그건 윈도우가 멀티 태스킹 환경이기 때문에 다른 영향을 받을 수 있기 때문이다. 그리고 windows 98 windows 2000 에서도 값이 다르게 나올 거라 생각한다. (직접 해보진 못했지만..)

그냥 상대적인 비교만으로 족할 듯 하다..

- the end of this article -

 

[출처] [펌] 정확한 시간 측정 방법|작성자 신의한수

댓글

이 블로그의 인기 게시물

각도 단위 변환

(1) 각도의 기본 단위 각도를 나타내는 단위입니다. 360분법 으로 표시하는 1도는 사방을 360으로 나눈 크기입니다. 1분은 1도를 60등분한 각이고 1초는 1분을 다시 60등분한 크기입니다. 분(arcminute)과 초(arcsecond)는 시간을 나타내는 단위인 분(minute), 초(second)와 기호가 같은데, 천문학(영어)에서는 둘을 구분하기 위해 각도는 나타내는 단위에는 ' arc- '를 붙여 표기합니다. 각도를 나타내는 다른 방법으로 호도법 이 있습니다. 호도법은 반지름에 대한 호의 길이 단위로 각도를 표시하는 방법으로 사방은 원주율(π)의 2배 크기가 됩니다(360° = 2π rad). 호도법으로 나타낸 각도는 라디안(radian)으로 표시하며, 360분법으로 나타낸 각도를 호도법으로 나타낸 각도로 바꾸어 주려면 360분법으로 나타낸 각도에 π/180을 곱해주면 됩니다. 컴퓨터 프로그램 언어에서 삼각함수를 계산할 때에는 주로 호도법은 쓰고 있습니다. 1도(˚ , degree) : 1˚ = 60′ = 3600″ = π/180 rad 1분(′, arcminute) : 1′ = 60″ = 1/60° 1초(″, arcsecond) : 1″ = 1/60′ = 1/3600° 1라디안(rad, radian) : 1 rad = 180/π° (π = 원주율 = 3.1415926535897932384626433832795) (2) 시간의 기본 단위 시간의 기본단위는 초(second)입니다. 1초는 국제 표준으로 정밀하게 정의되어 있는데, 세슘 원자(세슘-133)가 9,192,631,770번 진동하는 동안의 시간으로 정의되어 있습니다. 본래 1초는 1 평균 태양일의 1/86400로 정의되어 있었지만 지구의 자전 주기는 다소 불규칙하고 느리게 바뀌고 있으므로 균일한 시간을 정의하기에는 부족합니다. 이후 1초는 지구의 공전 주기를 바탕으로 다시 정의 되었다가 지금은 세슘 원자의 특성을 기반으로 새롭게 정의하여 ...

Cramer rule - 크래머 공식

  크래머 공식 (Cramer's rule)은 선형연립방정식의 해를 행렬식 으로 표현하는 선형대수학 의 정리(theorem)이다. 이름은 가브리엘 크래머 (Gabriel Cramer) (1704 - 1752)에게서 유래한다.     방정식이 많은 경우의 실제 해의 계산에 있어서는 그리 유용하지 않지만, 피봇팅(pivoting)이 필요하지 않은 경우 작은 크기의 행렬에서는 가우스 소거법보다 훨씬 효율적이다. 크래머 공식은 연립방정식의 해를 외재적으로 표현하기 때문에 이론의 전개에 유용하다.   연립방정식이 다음과 같은 행렬 간의 곱으로 표현될 때. A x = c   식에서 정사각행렬 (square matrix) A 는 역행렬을 갖고, 벡터 x 는 ( x i ) 를, 벡터 c 는 ( c i ) 를 성분으로 갖는 열벡터이다.   정리는 다음과 같다. 식에서 A i 는 A 의 i 번째 열을 열벡터 c 로 대체한 행렬을 말한다.     예 [ 편집 ] 2x2 행렬에서 공식을 적용해 보면, 주어진 연립방정식이 다음과 같을 때, a x + b y = e c x + d y = f , 이 식은 로 쓸 수 있으며, 공식을 적용하면, 이 된다.         미분기하학에 적용 [ 편집 ]   크래머 공식은 미분기하 문제를 풀 때 매우 유용하다. 두 개의 방정식 , 이라 가정한다. 여기서, u 와 v 는 독립 변수이고, , 라 정의한다.   여기서 의 방정식을 찾는 것은 크래머 공식으로 해결할 수 있다.   첫째 , F,G,x,y 의 미분을 계산한다. dF, dG 에 dx 와 dy 를 대입하면   u 와...

CTE(Coefficient of Thermal Expansion: 열팽창 계수)

열변형량 계산표.xlsx         측정기기에 대해서 1. 사용 환경은 20℃으로 설정하지 않으면 안되나요?   그런 일은 없겠지만, 물건은 온도가 높아지면 팽창하고, 낮아지면 줄어듭니다. 거기서 공업적 길이를 나타내는 경우에 표준 온도 20℃으로 결정해 그 온도에 있어서의 결과를 나타내게 되어 있습니다. 그것은 ISO 1에 의해, 「길이 측정의 표준 온도는 20℃으로 한다」라고 규정되고 있습니다. 단, 이 규격에는 20℃에 대한 허용치는 나타나지 않습니다만, 별도인 규격으로 예를 들면 JIS Z 8703에서는 광공업에 있어서의 시험(측정이나 측정기의 교정도 포함된다)을 실시하는 장소의 온도에 관한 표준 상태의 허용차이가 규정되고 있습니다.   표준 온도의 허용차이 급별 허용차이 ℃ 온도 0. 5급 ± 0.5 온도 1급 ± 1 온도 2급 ± 2 온도 5급 ± 5 온도 15급 ± 15   즉, 항상 20℃에 정확히 맞추는 것은 매우 곤란해서, 어느 허용차이의 온도 환경이 필요하게 될까는 어느 정도의 정확한 측정이나 시험을 실시하느냐에 달려있다고 봅니다. 고정밀도의 측정을 실시하려면 , 허용차이의 작은 온도 환경이 요구됩니다.   2. 선열팽창 계수를 가르쳐 주세요?   물건은 온도가 높아지면 팽창하고 낮아지면 줄어듭니다. 온도 변화에 의한 물체의 길이의 증감을 수치화한 것이 선열팽창 계수입니다. 아래와 같이에 당사제품의 선열팽창 계수를 몇개인가 올려 보고 싶다고 생각합니다. 제품명 정밀도에 영향을 주는 주요 부분의 재질 선열팽창 계수 게이지 블록(스틸) 강 10.8 X 10 -6 K -1 게이지 블록(세라믹) ...