프로그램의 특정 구간의 성능 또는 알고리즘의 성능 측정이 필요한 경우가 있습니다. 보통 시간을 측정하는데 C#에는 시간을 측정하는 다양한 함수들이 있습니다. 그중에서 대표적인 세 가지 (DateTime.UtcNow.Ticks, Stopwatch, Environment.TickCount) 에 대해 알아봅니다.
✅ 처리 속도 측정 방법 세 가지
DateTime.UtcNow.Ticks
UTC 시간을 사용하여 측정하는 방식입니다. Tick 은 1초를 10억 개로 나눈 것으로 정밀한 측정이 가능합니다. 현재 시간을 사용하기 때문에 성능 오버헤드가 매우 낮습니다. DateTime.Now 와 유사하지만 UTC 시간대를 사용하므로 지역 문제가 없습니다.
단점
- 시스템의 현재 시간을 가져오는 것으로 시간을 변경하거나 타임존이 변경되면 측정된 시간에 영향을 줍니다.
- 시스템 부하가 높을 경우 측정된 결과를 보장할 수 없습니다.
Example
public int DateTimeUtcNowTicksExample()
{
long startTicks = DateTime.UtcNow.Ticks;
// 측정할 코드
long endTicks = DateTime.UtcNow.Ticks;
// 측정된 시간 출력
long elapsedTicks = endTicks - startTicks;
TimeSpan elapsedSpan = new TimeSpan(elapsedTicks);
Console.WriteLine("측정된 시간: {0}ms", elapsedSpan.TotalMilliseconds);
return elapsedSpan.TotalMilliseconds;
}
Stopwatch
Stopwatch 는 내부적으로 WinAPI 함수인 QueryPerformanceCounter() 를 사용합니다. 세 가지 중 가장 정밀한 측정이 가능하지만 정확한 측정을 위해서는 아래의 단점들을 고려해서 사용해야 합니다.
단점
- 정밀한 시간 측정이 어렵습니다. CPU Frequency, 부하 상태, 운영 체제 등에 영향을 받기 때문에 정밀한 시간 측정을 보장할 수 없습니다.
- 멀티코어 CPU에서 사용하면 다른 코어와의 동기화 오버헤드로 인해 성능 저하가 발생할 수 있습니다.
Example
public int StopwatchExample()
{
Stopwatch stopwatch = new Stopwatch();
// 시간 측정 시작
stopwatch.Start();
// 측정할 코드
// 시간 측정 종료
stopwatch.Stop();
// 측정된 시간 출력
Console.WriteLine("실행 시간: " + stopwatch.ElapsedMilliseconds + "밀리초");
return stopwatch.ElapsedMilliseconds;
}
Environment.TickCount
Environment.TickCount는 내부적으로 WinAPI 함수인 GetTickCount()를 사용합니다. 가장 낮은 정밀도를 보여줍니다.
단점
- 시스템 시작 후 경과된 시간을 32비트 정수로 표현합니다. 그러므로 최대 약 49.7일(2^31 ms)까지만 측정할 수 있습니다.
- 실제 정밀도는 약 15.6ms 로 ms 이하의 시간 단위까지 측정해야 한다면 다른 측정 방식을 사용해야 합니다.
Example
public int EnvironmentTickCountExample()
{
int start = Environment.TickCount;
// 측정할 코드
int end = Environment.TickCount;
return end - start;
}
세 가지 성능 비교
stackoverflow.com 에 위 함수들에 대해 비교한 내용이 있습니다.
using System.Diagnostics;
namespace Counters
{
internal class Program
{
static void Main(string[] args)
{
int xcnt = 0;
long xdelta, xstart;
xstart = DateTime.UtcNow.Ticks;
do
{
xdelta = DateTime.UtcNow.Ticks - xstart;
xcnt++;
} while (xdelta == 0);
Console.WriteLine("DateTime:\t{0} ms, in {1} cycles", xdelta / (10000.0), xcnt);
int ycnt = 0, ystart;
long ydelta;
ystart = Environment.TickCount;
do
{
ydelta = Environment.TickCount - ystart;
ycnt++;
} while (ydelta == 0);
Console.WriteLine("Environment:\t{0} ms, in {1} cycles ", ydelta, ycnt);
Stopwatch sw = new Stopwatch();
int zcnt = 0;
long zstart, zdelta;
sw.Start();
zstart = sw.ElapsedTicks; // This minimizes the difference (opposed to just using 0)
do
{
zdelta = sw.ElapsedTicks - zstart;
zcnt++;
} while (zdelta == 0);
sw.Stop();
Console.WriteLine("StopWatch:\t{0} ms, in {1} cycles", (zdelta * 1000.0) / Stopwatch.Frequency, zcnt);
Console.ReadKey();
}
}
}
실행하면 아래와 같은 결과가 나옵니다.
DateTime: 0.0136 ms, in 1 cycles
Environment: 16 ms, in 1851391 cycles
StopWatch: 0.001 ms, in 1 cycles
실행 시간을 측정하는 세 가지 방법에 대해 알아봤습니다.