Timer - 시간 측정기

C++ 2019. 12. 25. 09:32
반응형

우선 알고리즘이나 STL의 효율을 알아보기 위해 타이머를 제작할 것이다. 코딩에서 효율이란 물리적으로 프로그래머가 코딩 양을 얼마나 효율적으로 작성하였는가를 물을 수도 있겠지만, 이번 시간에는 얼마나 짧은 시간만에 같은 동작을 할것이냐에 대한 물음이다. 어짜피 SLT은 자료를 담는 템플릿 클래스이기 때문에 정렬이나 구동 목적자체(저장, 삭제, 수정 등 데이터 관리)는 비슷한 경우일 가능성이 매우 높다. 따라서 코드 내부적으로 얼마나 짧은 시간 내에 같은 일을 수행하는지에 초점을 마추는 것이다.

 

그 [시간]을 기준으로 STL이나 알고리즘을 평가할 예정이라면, 당연히 그 시계가 정확해야 공평할 것이다. 이번 포스팅은 각각의 수행시간을 정확히 측정하기 위한 시계를 작성할 것이다.

 

 

QueryPerformanceCounter 함수

 

QueryPerformanceCounter function - Win32 apps

Retrieves the current value of the performance counter, which is a high resolution (<1us) time stamp that can be used for time-interval measurements.

docs.microsoft.com

(<1us[1마이크로초]) 보다 큰 시간간격을 측정하기 위해 현재 수행 횟수의 값을 가져옵니다.

구문

 
1
2
3
BOOL QueryPerformanceCounter(
  LARGE_INTEGER *lpPerformanceCount
);
 
 

 

인자

lpPerformanceCount

현재 성능 횟수의 값을 받는 변수에 대한 포인터입니다.

반환 값

반환에 성공하면 0이 아닌 값을 반환합니다.

만약 실패할 시에는 0을 반환합니다. 더욱 많은 정보를 확인하고 싶으면 GetLastError을 호출하세요. 윈도우 XP 혹은 이후의 버전 운영체제에서는 해당 함수가 언제나 성공할 것이며, 즉, 0을 반환할 일이 없을 것입니다.

 

 

 

QueryPerformanceFrequency 함수

성능 주기의 빈도 값(frequency of the performance counter)을 가져옵니다. 성능 주기 빈도 값이란, 시스탬 부팅 시 고정되어 있으며, 모든 프로세서에 일관된 값입니다. 그로므로, 주기(frequency)는 응용프로그램 초기화 시에만 구하면 되며, 결과값은 임시저장(cached)될 수 있습니다.

구문

1
2
3
BOOL QueryPerformanceFrequency(
  LARGE_INTEGER *lpFrequency
);
 

인자

lpFrequency

현재 성능 주기의 빈도(frequency of the performance counter)를 초단위로 받는 변수에 대한 포인터입니다. 만약 하드웨어에 세밀한 성능 측정기(high-resolution performance counter)를 지원하지 않는다면 이 매개변수는 0일 수 있습니다. (윈도우 XP 혹은 이상을 실행하는 OS에서는 발생하지 않습니다.)

반환 값

하드웨어가 세밀한 측정기(high-resolution performance counter)를 지원하면 0이 아닌 값을 반환합니다.

만약 실패할 경우 0을 반환합니다. 더욱 많은 정보를 확인하고 싶으면 GetLastError을 호출하세요. 윈도우 XP 혹은 이후의 버전 운영체제에서는 해당 함수가 언제나 성공할 것이며, 즉, 0을 반환할 일이 없을 것입니다.

 

 

 

주석

이 함수와 기능을 더 알고 싶다면 Acquiring high-resolution time stamps을, 참고하세요.

요구사항

최소 Client 사양 Windows 2000 Professional [desktop apps | UWP apps]
최소 Server 사양 Windows 2000 Server [desktop apps | UWP apps]
운영체제 Windows
헤더 profileapi.h (include Windows.h)
라이브러리 Kernel32.lib
DLL Kernel32.dll

 

 

 

 

예제

 

Acquiring high-resolution time stamps - Win32 apps

Acquiring high-resolution time stamps In this article --> Windows provides APIs that you can use to acquire high-resolution time stamps or measure time intervals. The primary API for native code is QueryPerformanceCounter (QPC). For device drivers, the ker

docs.microsoft.com

활용예제_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds;
LARGE_INTEGER Frequency;
 
QueryPerformanceFrequency(&Frequency); 
QueryPerformanceCounter(&StartingTime);
 
// Activity to be timed // 어떤 행위
 
QueryPerformanceCounter(&EndingTime);
ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart;
 
 
//
// We now have the elapsed number of ticks, along with the
// number of ticks-per-second. We use these values
// to convert to the number of elapsed microseconds.
// To guard against loss-of-precision, we convert
// to microseconds *before* dividing by ticks-per-second.
//
 
ElapsedMicroseconds.QuadPart *= 1000000;
ElapsedMicroseconds.QuadPart /= Frequency.QuadPart;
 

21번줄 1000000을 곱하는 이유는... 해당 함수가 측정값 tick 수(처리한 양)를 표시하는데, 이 값을 사용하여 주기(초당 tick)로 나누어 주어야 시간이 계산된다. 위 과정에서 정밀도 상실을 방지하기 위해 마이크로세컨드(us)로 곱한 뒤 계산하는 것이다.

 

 

실험예제_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <Windows.h>
#include <iostream>
 
 
int main()
{
    LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds;
    LARGE_INTEGER Frequency;
 
    QueryPerformanceFrequency(&Frequency);
    QueryPerformanceCounter(&StartingTime);
 
 
    int  a = 0;
    for (size_t i = 0; i < 100000; i++)
    {
        a = i;
    }
 
 
    QueryPerformanceCounter(&EndingTime);
    ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart;
 
    ElapsedMicroseconds.QuadPart *= 1000000;
    ElapsedMicroseconds.QuadPart /= Frequency.QuadPart;
 
 
 
    std::cout << "Frequency: " << Frequency.QuadPart << std::endl;
    std::cout << "Start: " << StartingTime.QuadPart << std::endl;
    std::cout << "Ending: " << EndingTime.QuadPart << std::endl;
    std::cout << "Elapse: " << ElapsedMicroseconds.QuadPart << std::endl;
 
    return 0;
}
 
 

 

결과_

1
2
3
4
Frequency: 3410088
Start: 5740458981220
Ending: 5740458981876
Elapse: 192
 

여기에서 Elapse가 어떤 행위의 측정값이 되는 것이며, 이 형태를 클래스의 멤버함수로 담아서 사용하면 더 편할 것이다.

 

클래스 예제_

 

Header_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#pragma once
#include <Windows.h>
 
#define NAME 64
 
class Timer
{
private:
    LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds;
    LARGE_INTEGER Frequency;
    char m_Name[NAME];
 
public:
    Timer(const char* _Name);
    ~Timer();
 
public:
    void start();
    void stop();
    void log();
    const long double& elapsed_time() const
    {
        return  ElapsedMicroseconds.QuadPart * .000001;
    }
};
 
 

Cpp_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include "Timer.h"
#include <Windows.h>
#include <iostream>
 
Timer::Timer(const char* _Name) :
    StartingTime(LARGE_INTEGER()),
    EndingTime(LARGE_INTEGER()),
    ElapsedMicroseconds(LARGE_INTEGER()),
    Frequency(LARGE_INTEGER())
{
    for (size_t i = 0; i < NAME; i++)
    {
        m_Name[i] = _Name[i];
    }
}
 
Timer::~Timer()
{
}
 
 
 
void Timer::start()
{
    QueryPerformanceFrequency(&Frequency);
    QueryPerformanceCounter(&StartingTime);
}
 
void Timer::stop()
{
    QueryPerformanceCounter(&EndingTime);
    ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart;
 
 
    ElapsedMicroseconds.QuadPart *= 1000000;
    ElapsedMicroseconds.QuadPart /= Frequency.QuadPart;
}
 
void Timer::log()
{
    std::cout << m_Name << ": " << ElapsedMicroseconds.QuadPart * .000001 << "sec"<< std:: endl;
}
 
 

 

사용법_

1
2
3
4
5
6
Timer t1("TEST");
    
// 수행 
 
 

 

4번줄 수행 주석부분에 측정하고픈 코드를 써넣으면된다. 나중에 로그로 찍을지 값으로 가져올지는 본인의 판단이다.

ㅇㅇ

 

 

최종 예제 결과_

1
2
3
4
5
TEST: 0.000768sec
Frequency: 3410088
Start: 5750430823251
Ending: 5750430825871
Elapse: 768us
 

이 포스팅에서 사용하는 함수는 모두 CPU의 처리속도를 기반으로 측정되는 수행 시간이다. 따라서, 코드를 수행하는 게 CPU가 될 수 밖에 없으므로, 그 CPU를 측정하는 것이 가장 세밀하고 정확한 측정이 될 수 밖에 없다. 하지만 역시 측정간 오차는 존재할 것이다. 물론 그 오차는 CPU를 측정할 때 생기는 측정코드 정도... 즉, 재는대에 시간이 걸린다는 뜻이다. 이 오차는 사실 상 무시할 수치다.

반응형

'C++' 카테고리의 다른 글

volatile  (0) 2019.12.25
저장소 클래스 - Storage classes (static, extern, thread_local, register)  (0) 2019.12.25
const*, constexpr**  (0) 2019.12.25
Scope - 범위  (0) 2019.12.25
선언지정자 - 정의 및 개요 (헷갈리는 선언 포함)  (0) 2019.12.25
Posted by Kestone Black Box
,