program tip

Environment.TickCount 대 DateTime.Now

radiobox 2020. 11. 29. 10:10
반응형

Environment.TickCount 대 DateTime.Now


Environment.TickCount시간 범위를 계산하는 데 사용할 수 있습니까?

int start = Environment.TickCount;
// Do stuff
int duration = Environment.TickCount - start;
Console.WriteLine("That took " + duration " ms");

TickCount서명되고 25 일 후에 롤오버 되므로 (32 비트를 모두 맞추려면 50 일이 걸리지 만 수학을 이해하려면 서명 된 비트를 스크랩해야 함) 유용하기에는 너무 위험 해 보입니다.

DateTime.Now대신 사용 하고 있습니다. 이것이 최선의 방법입니까?

DateTime start = DateTime.Now;
// Do stuff
TimeSpan duration = DateTime.Now - start;
Console.WriteLine("That took " + duration.TotalMilliseconds + " ms");

스톱워치 클래스를 사용하십시오. msdn에는 괜찮은 예가 있습니다. http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx

    Stopwatch stopWatch = Stopwatch.StartNew();
    Thread.Sleep(10000);
    stopWatch.Stop();
    // Get the elapsed time as a TimeSpan value.
    TimeSpan ts = stopWatch.Elapsed;

Environment.TickCountGetTickCount () WinAPI 함수를 기반으로 합니다. 밀리 초 단위이지만 실제 정밀도는 약 15.6ms입니다. 따라서 더 짧은 시간 간격을 측정 할 수 없습니다 (또는 0이됩니다).

참고 : 반환 된 값은 Int32이므로이 카운터는 49.7 일마다 롤오버됩니다. 이렇게 긴 간격을 측정하는 데 사용해서는 안됩니다.

DateTime.TicksGetSystemTimeAsFileTime () WinAPI 함수를 기반으로 합니다. 그것은 100 초 나노초 (10 분의 1 마이크로 소콘)입니다. DateTime.Ticks의 실제 정밀도는 시스템에 따라 다릅니다. XP에서 시스템 클럭의 증가는 Environment.TickCount에서와 같은 약 15.6ms입니다. Windows 7에서 정밀도는 1ms (Environemnt.TickCount는 여전히 15.6ms)이지만 절전 체계를 사용하는 경우 (일반적으로 랩톱에서) 15.6ms로 낮출 수도 있습니다.

StopwatchQueryPerformanceCounter () WinAPI 함수를 기반으로 합니다 (그러나 시스템에서 고해상도 성능 카운터를 지원하지 않는 경우 DateTime.Ticks가 사용됨)

StopWatch를 사용하기 전에 두 가지 문제를 확인하십시오.

  • 다중 프로세서 시스템에서는 신뢰할 수 없을 수 있습니다 (MS kb895980 , kb896256 참조 ).
  • CPU 주파수가 다르면 신뢰할 수 없습니다 ( 기사 읽기 ).

간단한 테스트로 시스템의 정밀도를 평가할 수 있습니다.

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();
}

롤오버가 걱정되는 이유는 무엇입니까? 측정하는 기간이 24.9 일 미만이고 상대적인 기간 을 계산하는 한 괜찮습니다. 실행 시간의 일부에만 관심이있는 한 (시작점과 끝점에서보다 작거나 큰 비교를 직접 수행하는 것과는 대조적으로) 시스템이 실행 된 기간은 중요하지 않습니다. 즉 :

 int before_rollover = Int32.MaxValue - 5;
 int after_rollover = Int32.MinValue + 7;
 int duration = after_rollover - before_rollover;
 Console.WriteLine("before_rollover: " + before_rollover.ToString());
 Console.WriteLine("after_rollover: " + after_rollover.ToString());
 Console.WriteLine("duration: " + duration.ToString());

올바르게 인쇄합니다.

 before_rollover: 2147483642
 after_rollover: -2147483641
 duration: 13

부호 비트에 대해 걱정할 필요가 없습니다. C와 마찬가지로 C #은 CPU가이를 처리하도록합니다.

이것은 임베디드 시스템에서 시간 계산과 함께 이전에 겪은 일반적인 상황입니다. 예를 들어 beforerollover <afterrollover를 직접 비교하지 않습니다. 나는 항상 빼기를 수행하여 롤오버를 고려한 기간을 찾은 다음 기간을 기준으로 다른 계산을 수행합니다.


당신은 아마 원할 것입니다 System.Diagnostics.StopWatch.


의 기능을 찾고 Environment.TickCount있지만 새 Stopwatch개체 를 만드는 오버 헤드가없는 경우 정적 Stopwatch.GetTimestamp()메서드 (와 함께 Stopwatch.Frequency)를 사용하여 긴 시간 범위를 계산할 수 있습니다. GetTimestamp()반환 하기 때문에 long매우 오랜 시간 동안 오버플로되지 않습니다 (이를 작성하는 데 사용하는 컴퓨터에서 100,000 년 이상). 또한 Environment.TickCount최대 해상도가 10 ~ 16 밀리 초인 것보다 훨씬 더 정확 합니다.


사용하다

System.Diagnostics.Stopwatch

라는 속성이 있습니다.

EllapsedMilliseconds

Environment.TickCount는 다른 솔루션보다 훨씬 빠른 것 같습니다.

Environment.TickCount 71
DateTime.UtcNow.Ticks 213
sw.ElapsedMilliseconds 1273

측정 값은 다음 코드에 의해 생성되었습니다.

static void Main( string[] args ) {
    const int max = 10000000;
    //
    //
    for ( int j = 0; j < 3; j++ ) {
        var sw = new Stopwatch();
        sw.Start();
        for ( int i = 0; i < max; i++ ) {
            var a = Environment.TickCount;
        }
        sw.Stop();
        Console.WriteLine( $"Environment.TickCount {sw.ElapsedMilliseconds}" );
        //
        //
        sw = new Stopwatch();
        sw.Start();
        for ( int i = 0; i < max; i++ ) {
            var a = DateTime.UtcNow.Ticks;
        }
        sw.Stop();
        Console.WriteLine( $"DateTime.UtcNow.Ticks {sw.ElapsedMilliseconds}" );
        //
        //
        sw = new Stopwatch();
        sw.Start();
        for ( int i = 0; i < max; i++ ) {
            var a = sw.ElapsedMilliseconds;
        }
        sw.Stop();
        Console.WriteLine( $"sw.ElapsedMilliseconds {sw.ElapsedMilliseconds}" );
    }
    Console.WriteLine( "Done" );
    Console.ReadKey();
}

다음은이 스레드에서 가장 유용한 답변과 의견이 무엇인지, 추가 벤치 마크 및 변형에 대한 업데이트 및 새로 고침 요약입니다.

먼저, 다른 사람들이 의견에서 지적했듯이 지난 몇 년 동안 "현대적인"Windows (Win XP ++) 및 .NET 및 최신 하드웨어에서 상황이 바뀌 었습니다. Stopwatch ()를 사용하지 않을 이유가 거의 없습니다. 자세한 내용은 MSDN 을 참조하십시오. 인용 :

"QPC 정확도는 전원 관리 또는 Turbo Boost 기술로 인한 프로세서 주파수 변경의 영향을 받습니까?
아니요 . 프로세서에 고정 TSC가있는 경우 QPC는 이러한 종류의 변경에 영향을받지 않습니다. 프로세서에 고정 TSC가없는 경우 QPC는 프로세서 주파수 변경 또는 Turbo Boost 기술의 영향을받지 않는 플랫폼 하드웨어 타이머로 되돌아갑니다.

QPC는 멀티 프로세서 시스템, 멀티 코어 시스템 및 하이퍼 스레딩이있는 시스템에서 안정적으로 작동합니까?

QPC가 내 컴퓨터에서 작동하는지 어떻게 확인하고 검증합니까?
이러한 검사를 수행 할 필요가 없습니다.

불변하지 않는 TSC가있는 프로세서는 무엇입니까? [.. 더 읽어보기 ..] "

그러나 Stopwatch ()의 정밀도가 필요하지 않거나 적어도 Stopwatch의 성능 (정적 대 인스턴스 기반) 및 기타 가능한 변형에 대해 정확히 알고 싶다면 계속 읽으십시오.

cskwg에서 위의 벤치 마크를 인수하고 더 많은 변형을 위해 코드를 확장했습니다. 몇 년 된 i7 4700 MQ 및 C # 7 with VS 2017로 측정했습니다. 특히 Stopwatch () 성능이 언급 된 벤치 마크에 비해 개선 된 것 같습니다.

이것은 루프에서 1,000 만 번 반복되는 결과의 예입니다. 항상 그렇듯이 절대 값은 중요하지 않지만 상대 값도 다른 하드웨어에서 다를 수 있습니다.

32 비트, 최적화없는 릴리스 모드 :

측정 : GetTickCount64 () [ms] : 275
측정 : Environment.TickCount [ms] : 45
측정 : DateTime.UtcNow.Ticks [ms] : 167
측정 : 스톱워치 : .ElapsedTicks [ms] : 277
측정 : 스톱워치 : .ElapsedMilliseconds [ ms] : 548
측정 됨 : 정적 Stopwatch.GetTimestamp [ms] : 193
측정 됨 : Stopwatch + DateTime으로 변환 [ms] : 551
DateTime.Now.Ticks [ms]와 비교 : 9010

32 비트, 릴리스 모드, 최적화 :

측정 : GetTickCount64 () [ms] : 198
측정 : Environment.TickCount [ms] : 39
측정 : DateTime.UtcNow.Ticks [ms] : 66 (!)
측정 : 스톱워치 : .ElapsedTicks [ms] : 175
측정 : 스톱워치 : .ElapsedMilliseconds [ms] : 491
측정 됨 : static Stopwatch.GetTimestamp [ms] : 175
측정 됨 : Stopwatch + DateTime으로 변환 [ms] : 510
DateTime.Now.Ticks [ms]와 비교 : 8460

64 비트, 최적화없는 릴리스 모드 :

측정 : GetTickCount64 () [ms] : 205
측정 : Environment.TickCount [ms] : 39
측정 : DateTime.UtcNow.Ticks [ms] : 127
측정 : 스톱워치 : .ElapsedTicks [ms] : 209
측정 : 스톱워치 : .ElapsedMilliseconds [ ms] : 285
측정 됨 : 정적 Stopwatch.GetTimestamp [ms] : 187
측정 됨 : Stopwatch + DateTime으로 변환 [ms] : 319
DateTime.Now.Ticks [ms]와 비교 : 3040

64 비트, 릴리스 모드, 최적화 :

측정 됨 : GetTickCount64 () [ms] : 148
측정 됨 : Environment.TickCount [ms] : 31 (아직 가치가 있습니까?)
측정 됨 : DateTime.UtcNow.Ticks [ms] : 76 (!)
측정 됨 : 스톱워치 : .ElapsedTicks [ ms] : 178
측정 됨 : 스톱워치 : .ElapsedMilliseconds [ms] : 226
측정 됨 : 정적 Stopwatch.GetTimestamp [ms] : 175
측정 됨 : 스톱워치 + 날짜 시간으로 변환 [ms] : 246
DateTime.Now.Ticks [ms]와 비교 : 3020

매우 흥미로울 수 있습니다 . 스톱워치 시간을 인쇄하기 위해 DateTime 값을 만드는 데 거의 비용이 들지 않는 것 같습니다 . 실용적인 방법보다 더 학문적 인 방법에서 흥미로운 점은 정적 스톱워치가 (예상대로) 약간 빠르다는 것입니다. 일부 최적화 포인트는 매우 흥미 롭습니다. 예를 들어, 32 비트 만있는 Stopwatch.ElapsedMilliseconds가 다른 변형 (예 : 정적 변형)에 비해 느린 이유를 설명 할 수 없습니다. This and DateTime. 이제 64 비트로 속도가 두 배 이상입니다.

수백만 번의 실행에 대해서만 스톱워치의 시간이 중요하기 시작합니다. 이것이 사실이라면 (그러나 너무 일찍 마이크로 최적화에주의하십시오) GetTickCount64 ()를 사용하면 흥미로울 수 있지만 특히 DateTime.UtcNow 에서는 Stopwatch보다 정확도가 낮지 만 더 빠른 64 비트 (긴) 타이머가 있습니다. , 32 비트 "추악한"Environment.TickCount를 엉망으로 만들 필요가 없습니다.

예상대로 DateTime.Now는 가장 느립니다.

실행하면 코드는 현재 스톱워치 정확도 등을 검색합니다.

다음은 전체 벤치 마크 코드입니다.

using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using static System.Environment;

[...]

    [DllImport("kernel32.dll") ]
    public static extern UInt64 GetTickCount64(); // Retrieves a 64bit value containing ticks since system start

    static void Main(string[] args)
    {
        const int max = 10_000_000;
        const int n = 3;
        Stopwatch sw;

        // Following Process&Thread lines according to tips by Thomas Maierhofer: https://codeproject.com/KB/testing/stopwatch-measure-precise.aspx
        // But this somewhat contradicts to assertions by MS in: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396#Does_QPC_reliably_work_on_multi-processor_systems__multi-core_system__and_________systems_with_hyper-threading
        Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1); // Use only the first core
        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
        Thread.CurrentThread.Priority = ThreadPriority.Highest;
        Thread.Sleep(2); // warmup

        Console.WriteLine($"Repeating measurement {n} times in loop of {max:N0}:{NewLine}");
        for (int j = 0; j < n; j++)
        {
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var tickCount = GetTickCount64();
            }
            sw.Stop();
            Console.WriteLine($"Measured: GetTickCount64() [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var tickCount = Environment.TickCount; // only int capacity, enough for a bit more than 24 days
            }
            sw.Stop();
            Console.WriteLine($"Measured: Environment.TickCount [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = DateTime.UtcNow.Ticks;
            }
            sw.Stop();
            Console.WriteLine($"Measured: DateTime.UtcNow.Ticks [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = sw.ElapsedMilliseconds;
            }
            sw.Stop();
            Console.WriteLine($"Measured: Stopwatch: .ElapsedMilliseconds [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = Stopwatch.GetTimestamp();
            }
            sw.Stop();
            Console.WriteLine($"Measured: static Stopwatch.GetTimestamp [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            DateTime dt=DateTime.MinValue; // just init
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = new DateTime(sw.Elapsed.Ticks); // using variable dt here seems to make nearly no difference
            }
            sw.Stop();
            //Console.WriteLine($"Measured: Stopwatch+conversion to DateTime [s] with millisecs: {dt:s.fff}");
            Console.WriteLine($"Measured: Stopwatch+conversion to DateTime [ms]:  {sw.ElapsedMilliseconds}");

            Console.WriteLine();
        }
        //
        //
        sw = new Stopwatch();
        var tickCounterStart = Environment.TickCount;
        sw.Start();
        for (int i = 0; i < max/10; i++)
        {
            var a = DateTime.Now.Ticks;
        }
        sw.Stop();
        var tickCounter = Environment.TickCount - tickCounterStart;
        Console.WriteLine($"Compare that with DateTime.Now.Ticks [ms]: {sw.ElapsedMilliseconds*10}");

        Console.WriteLine($"{NewLine}General Stopwatch information:");
        if (Stopwatch.IsHighResolution)
            Console.WriteLine("- Using high-resolution performance counter for Stopwatch class.");
        else
            Console.WriteLine("- Using high-resolution performance counter for Stopwatch class.");

        double freq = (double)Stopwatch.Frequency;
        double ticksPerMicroSec = freq / (1000d*1000d) ; // microsecond resolution: 1 million ticks per sec
        Console.WriteLine($"- Stopwatch accuracy- ticks per microsecond (1000 ms): {ticksPerMicroSec:N1}");
        Console.WriteLine(" (Max. tick resolution normally is 100 nanoseconds, this is 10 ticks/microsecond.)");

        DateTime maxTimeForTickCountInteger= new DateTime(Int32.MaxValue*10_000L);  // tickCount means millisec -> there are 10.000 milliseconds in 100 nanoseconds, which is the tick resolution in .NET, e.g. used for TimeSpan
        Console.WriteLine($"- Approximated capacity (maxtime) of TickCount [dd:hh:mm:ss] {maxTimeForTickCountInteger:dd:HH:mm:ss}");
        // this conversion from seems not really accurate, it will be between 24-25 days.
        Console.WriteLine($"{NewLine}Done.");

        while (Console.KeyAvailable)
            Console.ReadKey(false);
        Console.ReadKey();
    }

대신 Stopwatch 클래스를 사용해야합니다 .


다음과 같은 이유로 Environment.TickCount를 사용합니다.

  1. Stopwatch 클래스는 Compact Framework에 없습니다.
  2. Stopwatch uses the same underlying timing mechanism as TickCount, so the results won't be any more or less accurate.
  3. The wrap-around problem with TickCount is cosmically unlikely to be hit (you'd have to leave your computer running for 27 days and then try to measure a time that just happens to span the wrap-around moment), and even if you did hit it the result would be a huge negative time span (so it would kind of stand out).

That being said, I would also recommend using Stopwatch, if it's available to you. Or you could take about 1 minute and write a Stopwatch-like class that wraps Environment.TickCount.

BTW, I see nothing in the Stopwatch documentation that mentions the wrap-around problem with the underlying timer mechanism, so I wouldn't be surprised at all to find that Stopwatch suffers from the same problem. But again, I wouldn't spend any time worrying about it.


I was going to say wrap it into a stopwatch class, but Grzenio already said the right thing, so I will give him an uptick. Such encapsulation factors out the decision as to which way is better, and this can change in time. I remember being shocked at how expensive it can be getting the time on some systems, so having one place that can implement the best technique can be very important.


For one-shot timing, it's even simpler to write

Stopwatch stopWatch = Stopwatch.StartNew();
...dostuff...
Debug.WriteLine(String.Format("It took {0} milliseconds",
                              stopWatch.EllapsedMilliseconds)));

I'd guess the cosmically unlikely wraparound in TickCount is even less of a concern for StopWatch, given that the ElapsedTicks field is a long. On my machine, StopWatch is high resolution, at 2.4e9 ticks per second. Even at that rate, it would take over 121 years to overflow the ticks field. Of course, I don't know what's going on under the covers, so take that with a grain of salt. However, I notice that the documentation for StopWatch doesn't even mention the wraparound issue, while the doc for TickCount does.

참고URL : https://stackoverflow.com/questions/243351/environment-tickcount-vs-datetime-now

반응형