문제

사용해도 괜찮을까요? 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;

다른 팁

환경 .TickCount 기반입니다 getTickCount () Winapi 기능. 밀리 초이지만 실제 정밀도는 약 15.6ms입니다. 따라서 짧은 시간 간격을 측정 할 수 없습니다 (또는 0이됩니다).

메모: 반환 된 값은 int32 이므로이 카운터는 ~ 49.7 일마다 롤링됩니다. 그러한 긴 간격을 측정하는 데 사용해서는 안됩니다.

dateTime.ticks 기반입니다 getSystemTimeAsFileTime() Winapi 함수. 100 년대 나노 초 (10 분의 1의 마이크로 소드)에 있습니다. DateTime.ticks의 실제 정밀도는 시스템에 따라 다릅니다. XP에서 시스템 클록의 증가는 약 15.6ms이며 환경에서와 동일합니다. Windows 7에서는 정밀도가 1ms (EnvironEmnt.tickcount 's는 여전히 15.6ms입니다)이지만 전력 저장 체계가 사용되면 (일반적으로 랩톱에서) 15.6ms로 줄어들 수 있습니다.

스톱워치 기반입니다 queryperformanceCounter () 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() 반환 a long, 그것은 매우 오랫동안 오랫동안 오랫동안 넘치지 않을 것입니다 (이 글을 쓰는 데 사용하는 기계에서 10 만 년 이상). 또한보다 훨씬 정확합니다 Environment.TickCount 최대 해상도는 10 ~ 16 밀리 초입니다.

사용

System.Diagnostics.Stopwatch

그것은 호텔이라는 속성이 있습니다

EllapsedMilliseconds

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

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

가장 먼저 : 다른 사람들이 의견에서 지적했듯이, 지난 몇 년 동안 상황이 바뀌었고 "현대"창 (xp ++)과 .NET을 사용하고 현대 하드웨어를 사용하여 StopWatch ()를 사용하지 않는 이유는 없습니다. 보다 MSDN 자세한 내용은. 인용 :

"QPC 정확도는 전력 관리 또는 터보 부스트 기술로 인한 프로세서 주파수 변경의 영향을 받습니까?
아니. 만약 프로세서에는 변하지 않는 TSC가 있습니다, QPC는 이러한 종류의 변화의 영향을받지 않습니다. 프로세서에 변하지 않는 TSC가없는 경우 QPC는 프로세서 주파수 변경 또는 터보 부스트 기술의 영향을받지 않는 플랫폼 하드웨어 타이머로 되돌아갑니다.

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

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

비 불변 TSC가있는 프로세서는 무엇입니까? [.. 더 읽어 ..] ""

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

CSKWG에서 위의 벤치 마크를 인수하고 더 많은 변형에 대한 코드를 확장했습니다. 나는 몇 년 된 i7 4700 mq 및 c# 7으로 측정했습니다. VS 2017 (더 정확하고 .NET 4.5.2로 컴파일 된 바이너리 리터럴에도 불구하고 C# 6입니다 : String Literals 및 'STATIC 사용 '). 특히 STOPWATCH () 성능은 언급 된 벤치 마크에 비해 개선 된 것으로 보입니다.

이것은 항상 루프에서 천만 반복의 결과의 예입니다. 항상 절대 값은 중요하지 않지만 상대 값조차 다른 하드웨어에서 다를 수 있습니다.

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

측정 : getTickCount64 () [MS] : 275
측정 : 환경 tickcount [ms] : 45
측정 : dateTime.utcNow.ticks [ms] : 167
측정 : STOPWATCH : .ELAPSEDTICKS [MS] : 277
측정 : STOPWATCH : .ELAPSEDMILLISECONDS [MS] : 548
측정 : static stopwatch.gettimestamp [ms] : 193
측정 : STOPWATCH+DATETIME [MS]로 변환 : 551
dateTime.now.ticks [ms]와 비교하십시오. 9010

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

측정 : getTickCount64 () [MS] : 198
측정 : 환경 tickcount [ms] : 39
측정 : DateTime.utcNow.ticks [MS] : 66 (!)
측정 : STOPWATCH : .ELAPSEDTICKS [MS] : 175
측정 : STOPWATCH : .ELAPSEDMILLISECONDS [MS] : 491
측정 : static stopwatch.gettimestamp [ms] : 175
측정 : STOPWATCH+DATETIME [MS]로의 변환 : 510
dateTime.now.ticks [ms]와 비교하십시오. 8460

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

측정 : getTickCount64 () [MS] : 205
측정 : 환경 tickcount [ms] : 39
측정 : dateTime.utcNow.ticks [ms] : 127
측정 : STOPWATCH : .ELAPSEDTICKS [MS] : 209
측정 : STOPWATCH : .ELAPSEDMILLISECONDS [MS] : 285
측정 : 정적 스톱워치 .gettimestamp [ms] : 187
측정 : STOPWATCH+DATETIME [MS]로 변환 : 319
dateTime.now.ticks [ms] : 3040과 비교하십시오

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

측정 : getTickCount64 () [MS] : 148
측정 : 환경 tickcount [ms] : 31 (여전히 그만한 가치가 있습니까?)
측정 : DateTime.utcNow.ticks [MS] : 76 (!)
측정 : STOPWATCH : .ELAPSEDTICKS [MS] : 178
측정 : STOPWATCH : .ELAPSEDMILLISECONDS [MS] : 226
측정 : static stopwatch.gettimestamp [ms] : 175
측정 : STOPWATCH+DATETIME [MS]로의 변환 : 246
dateTime.now.ticks [ms] : 3020과 비교하십시오

그것은 매우 흥미로울 수 있습니다 스톱워치 시간을 인쇄하기 위해 DateTime 값을 만들면 비용이 거의없는 것 같습니다.. 실제적인 방법보다 학문적으로 흥미로운 것은 정적 스톱워치가 약간 빠르므로 (예상대로). 일부 최적화 지점은 매우 흥미 롭습니다. 예를 들어, STOPWATCH.ELAPSEDMILLISECONDS 만 32 비트 만 사용하는 이유를 설명 할 수 없습니다. 이것과 dateTime.은 64 비트로 속도를 두 배 이상 두 배 이상 늘립니다.

당신은 볼 수 있습니다 : 수백만 건의 처형에 대해서만 스톱워치의 시간이 중요합니다. 이것이 사실이라면 (그러나 너무 일찍 마이크로 최적화를 조심해야한다면), gettickcount64 ()를 사용하면 특히 흥미로울 수 있습니다. dateTime.utcnow, 당신은 스톱워치보다 정밀도가 적지 만 더 빠르기 때문에 32 비트 "Ugly"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();
    }

당신은 사용해야합니다 스톱워치 대신 수업.

나는 환경을 사용합니다.

  1. 스톱워치 클래스는 컴팩트 프레임 워크가 아닙니다.
  2. STOPWATCH는 동일한 기본 타이밍 메커니즘을 TickCount와 사용하므로 결과는 더 정확하지 않습니다.
  3. TickCount의 랩 어라운드 문제는 우주적으로 타격을받지 않을 것입니다. (컴퓨터를 27 일 동안 실행 한 다음 랩 어라운드 순간에 걸쳐 발생하는 시간을 측정하려고 노력해야합니다). 그것은 눈에 띄게 될 것입니다).

즉, 스톱워치를 사용하는 것이 좋습니다. 또는 약 1 분이 걸리고 환경을 감싸는 스톱워치와 같은 클래스를 쓸 수도 있습니다.

BTW, 나는 기본 타이머 메커니즘의 랩 어라운드 문제를 언급하는 스톱워치 문서에서 아무것도 보지 못하므로 스톱워치가 같은 문제로 고통 받고 있다는 사실에 전혀 놀라지 않을 것입니다. 그러나 다시, 나는 그것에 대해 걱정하는 데 시간을 보내지 않을 것입니다.

나는 그것을 스톱워치 수업에 랩핑한다고 말했지만 Grzenio는 이미 올바른 말을했기 때문에 그에게 상승세를 줄 것입니다. 이러한 캡슐화는 어느 방식이 더 나은지 결정을 내립니다. 이는 시간이 변할 수 있습니다. 일부 시스템에서 시간을 보낼 수있는 비용이 얼마나 비싸고 충격을받은 것을 기억하므로 최상의 기술을 구현할 수있는 한 곳을 갖는 것이 매우 중요 할 수 있습니다.

원샷 타이밍의 경우 글을 쓰는 것이 훨씬 간단합니다.

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

나는 엘랩 스틱 필드가 길다는 점을 감안할 때 스톱워치에 대한 우주적으로 랩 어라운드가 그다지 우려하지 않는다고 생각합니다. 내 컴퓨터에서 스톱워치는 초당 2.4E9 진드기에서 고해상도입니다. 그 속도에도 불구하고 진드기 필드를 넘치는 데 121 년이 걸렸습니다. 물론, 나는 덮개 아래에서 무슨 일이 일어나고 있는지 알지 못하므로 소금 한 알로 가져 가십시오. 그러나 스톱워치에 대한 문서는 랩 어라운드 문제를 언급하지는 않지만 TickCount의 문서는 그렇게합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top