Development Tip

C# 고정밀도 타이머

MoonLight314 2024. 5. 14. 10:17
728x90

안녕하세요, MoonLight입니다.

고해상도 타이머 API

C#뿐만 아니라, Win32 API를 사용하는 Windows Application을 작성할 때 가끔씩 ms(밀리세컨드)보다 더 작은 us(마이크로세컨드)/ns(나노세컨드) 단위의 시간을 측정해야 하는 경우가 간혹 생깁니다.

저는 최근에 SSD의 정밀한 성능 측정을 수행하는 Application을 제작해야 하는 경우가 생겨서 이런 필요가 생겼습니다.

고정밀도 타이머를 사용하기 위한 API는 QueryPerformanceCounter()를 사용합니다.

 

https://learn.microsoft.com/ko-kr/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter

 

QueryPerformanceCounter 함수 - Win32 apps

시간 간격 측정에 사용할 수 있는 고해상도(<1us) 타임스탬프를 나타내는 성능 카운터의 현재 값을 검색합니다.

learn.microsoft.com

 

https://learn.microsoft.com/ko-kr/windows/win32/sysinfo/acquiring-high-resolution-time-stamps

 

고해상도 타임스탬프 획득 - Win32 apps

Windows는 고해상도 타임스탬프를 획득하거나 시간 간격을 측정하는 데 사용할 수 있는 API를 제공합니다.

learn.microsoft.com

 

QueryPerformanceCounter()는 PC가 부팅이후 몇 번의 Tick이 지났는지 Return해 줍니다.

값이 매우 커질수 있으므로 64 bit 길이의 변수를 Parameter로 사용합니다.

시간 측정 기본 Concept은 측정하기 전에 QueryPerformanceCounter()으로 현재 Tick Count를 저장하고,

시간을 측정하고자 하는 작업이 끝난 시점에 다시 QueryPerformanceCounter()를 Call해서 Tick Count를 구한후에 나중 Tick에서 예전 Tick 값을 빼서 해당 CPU의 Frequency값으로 나누면

초단위의 결과를 얻을 수 있습니다.

이 값을 1000을 곱하면 ms, 1000000을 곱하며 us 단위의 측정 시간을 얻을 수 있습니다.

이 때 중요한 것은 QueryPerformanceFrequency() API로 해당 System의 Frequency를 알아낸 다음에 이 값으로 나누어 주어야 합니다.

예전에는 이 값이 System마다 달랐는데, Windows 10 Build 1809+ 이후로는 10000000으로 고정값이 되었다고 하네요.

 

https://answers.microsoft.com/en-us/windows/forum/all/queryperformancefrequency-returns-10mhz-on-windows/44946807-5355-4b36-ba3e-43aa86ce30c0?auth=1

 

리디렉션 중

 

login.microsoftonline.com

 

편리해 졌다고 해야겠죠?

실제 Code

아래 Code는 실제로 매우 짧은 시간을 측정하는 C# Code입니다.

Console.WriteLine() 한 줄 실행하는 시간을 측정해 보는 Code입니다.

방법은 앞서 설명한 내용 그대로 입니다.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        [DllImport("Kernel32.dll")]
        public static extern bool QueryPerformanceCounter(out long lpPerformanceCount);

        static void Main(string[] args)
        {
            long startTicks, endTicks;
            double elapsedMilliseconds;

            QueryPerformanceCounter(out startTicks);    /* 시간 측정하기 전에 Tick Count 구합니다. */

            /* 시간을 측정하고자 하는 작업 */
            Console.WriteLine("Test\n\n");

            QueryPerformanceCounter(out endTicks);      /* 끝난 시점의 Tick Count 구합니다. */

            /* ms 단위로 변환 */
            elapsedMilliseconds = ((double)(endTicks - startTicks) / 10000000.0f) * 1000;

            Console.WriteLine("Elapsed Time : {0} ms", elapsedMilliseconds);
        }
    }
}
Test


Elapsed Time : 0.7932 ms

 

 

도움이 되셨다면 좋겠네요.

그럼, 다음에 또 만나요~!

728x90