Frage

Ist es überhaupt OK zu verwenden, um Zeit Spannweiten Environment.TickCountto berechnen?

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

Da TickCount unterzeichnet und wird nach 25 Tagen Rollover (er 50 Tage in Anspruch nimmt alle 32 Bits zu schlagen, aber Sie haben den signierten Bit Schrott, wenn Sie jeden Sinn für die Mathematik machen wollen), so scheint es, wie es zu riskant ist um nützlich zu sein.

Ich verwende DateTime.Now statt. Ist dies der beste Weg, dies zu tun?

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

Lösung

Mit Stoppuhr-Klasse. Es gibt ein gutes Beispiel auf 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;

Andere Tipps

Environment.TickCount basiert auf GetTickCount () WinAPI Funktion. Es ist in Millisekunden Aber die tatsächliche Genauigkeit ist etwa 15,6 ms. So können Sie nicht immer kürzeren Zeitabständen messen (oder Sie werden 0 erhalten)

Hinweis: Der Rückgabewert ist Int32, so dass diese Zähler rollt über je ~ 49,7 Tage. Sie sollten es nicht so lange Intervalle zu messen verwenden.

DateTime.Ticks basiert auf GetSystemTimeAsFileTime () WinAPI Funktion. Es ist in 100s ns (Zehntel microsoconds). Die tatsächliche Genauigkeit der DateTime.Ticks hängt vom System ab. Unter XP ist die Erhöhung des Systemtaktes etwa 15,6 ms, die gleiche wie in Environment.TickCount. Unter Windows 7 seine Präzision ist 1 ms (bei Environemnt.TickCount ist immer noch 15,6 ms ist), aber wenn ein Energiesparschema verwendet wird (in der Regel auf Laptops) als auch auf 15,6 ms nach unten gehen kann.

Stoppuhr basiert auf Queryperformancecounter () WinAPI-Funktion ( aber wenn hochauflösende Leistungsindikator wird nicht vom System unterstützt, DateTime.Ticks verwendet wird)

Vor der Verwendung von Stopwatch Mitteilung zwei Probleme:

  • kann auf Multi-Prozessor-Systemen unzuverlässig sein (siehe MS kb895980 , kb896256 )
  • kann es unzuverlässig sein, wenn die CPU-Frequenz ändert (lesen Sie dieses Artikel)

Sie können die Genauigkeit auf Ihrem System mit einfachen Test bewerten:

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

Warum sind Sie besorgt über Roll? Solange die Dauer Sie ist unter 24,9 Tage messen und berechnen Sie die relativ Dauer, Sie in Ordnung sind. Es spielt keine Rolle, wie lange das System bereits läuft, solange man sich nur betreffen mit Ihrem Teil dieser Laufzeit (als direkt gegenüber der Durchführung weniger als oder Größer-als-Vergleiche an dem Anfangs- und Endpunkt). D. h folgt aus:

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

richtig gedruckt:

 before_rollover: 2147483642
 after_rollover: -2147483641
 duration: 13

Sie müssen nicht um das Vorzeichenbit kümmern. C #, wie C, läßt die CPU damit umgehen.

Dies ist eine gemeinsame Situation, die ich in vor der Zeit zählt in Embedded-Systemen ausgeführt habe. Ich würde nie beforerollover vergleichen

Sie wollen wahrscheinlich System.Diagnostics.StopWatch .

Wenn Sie sich für die Funktionalität von Environment.TickCount aber ohne den Overhead der Schaffung neuer Stopwatch Objekte suchen, können Sie die statische Stopwatch.GetTimestamp() Methode verwenden (zusammen mit Stopwatch.Frequency) lange Zeiträume zu berechnen. Da GetTimestamp() eine long zurückkehrt, wird es nicht für eine sehr, sehr lange Zeit (mehr als 100.000 Jahre auf der Maschine zu schreiben ich verwende) überläuft. Es ist auch viel genauer als Environment.TickCount, die eine maximale Auflösung von 10 bis 16 Millisekunden haben.

Mit

System.Diagnostics.Stopwatch

Es hat eine Eigenschaft namens

EllapsedMilliseconds

Environment.TickCount scheint viel schneller als die anderen Lösungen zu sein:

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

Die Messungen wurden mit dem folgenden Code erzeugt:

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

Dies ist Art eines aktualisierten & frischt Zusammenfassung dessen, was die nützlichsten Antworten sein können und Kommentare in diesem Thread + extra Benchmarks und Varianten:

Das erste, was zuerst: Wie andere haben in den Kommentaren darauf hingewiesen, haben sich die Dinge in den letzten Jahren und mit „modernen“ Windows (Win XP ++) und .NET und moderner Hardware dort verändert sind keine oder nur eine geringe Gründen nicht Stoppuhr zu verwenden (). Siehe MSDN . Zitate:

  

"Ist QPC Genauigkeit durch den Prozessor Frequenzänderungen betroffen, verursacht durch Power-Management oder Turbo-Boost-Technologie?
   Nein. Wenn der Prozessor eine unveränderliche TSC hat, wird die QPC nicht durch diese Art von Veränderungen betroffen. Wenn der Prozessor keine unveränderlichen TSC hat, werden QPC auf ein Plattform-Hardware-Timer zurückgesetzt, die nicht durch den Prozessor Frequenzänderungen oder Turbo-Boost-Technologie betroffen sein wird.

     

Ist zuverlässig QPC auf Multi-Prozessor-Systemen arbeiten, Multi-Core-System und Systemen mit Hyper-Threading?
   Ja

     

Wie kann ich feststellen und überprüfen, ob QPC auf meinem Rechner funktioniert?
   Sie brauchen nicht solche Kontrollen durchzuführen.

     

Welche Prozessoren haben nicht invariant TSCs?   [..Lesen weiter ..]   „

Aber wenn Sie brauchen nicht die Präzision der Stoppuhr () oder zumindest wollen genau über die Leistung der Stoppuhr wissen (statisch vs. Instanz-basiert) und andere mögliche Varianten, lesen Sie weiter:

übernahm ich die Benchmark oben von cskwg und erweitert den Code für weitere Varianten. Ich habe mit einigen Jahren gemessen alten i7 4700 MQ und C # 7 mit VS 2017 (um genauer zu sein, kompiliert mit .NET 4.5.2 trotz binäre Literale ist es C # 6 (verwendet hierfür: Stringliterale und ‚Verwendung von statischen ‚). Vor allem scheint die Stoppuhr () Leistung im Vergleich zu dem genannten Benchmark verbessert werden.

Dies ist ein Beispiel der Ergebnisse von 10 Millionen Wiederholungen in einer Schleife, wie immer, absolute Werte sind nicht wichtig, sondern auch die relativen Werte auf anderer Hardware unterscheiden können:

32 Bit, Release-Modus ohne Optimierung:

  

Gemessen: GetTickCount64 () [ms]: 275
  Gemessen: Environment.TickCount [ms]: 45
  Gemessen: DateTime.UtcNow.Ticks [ms]: 167
  Gemessen: Stoppuhr: .ElapsedTicks [ms]: 277
  Gemessen: Stoppuhr: .ElapsedMilliseconds [ms]: 548
  Gemessen: statischer Stopwatch.GetTimestamp [ms]: 193
  Gemessen: Stoppuhr + Umstellung auf Datetime [ms]: 551
  Vergleichen Sie das mit DateTime.Now.Ticks [ms]: 9010

32 Bit, Release-Modus, optimiert:

  

Gemessen: GetTickCount64 () [ms]: 198
  Gemessen: Environment.TickCount [ms]: 39
  Gemessen: DateTime.UtcNow.Ticks [ms]: (!) 66
  Gemessen: Stoppuhr: .ElapsedTicks [ms]: 175
  Gemessen: Stoppuhr: .ElapsedMilliseconds [ms]: 491
  Gemessen: statischer Stopwatch.GetTimestamp [ms]: 175
  Gemessen: Stoppuhr + Umstellung auf Datetime [ms]: 510
  Vergleichen Sie das mit DateTime.Now.Ticks [ms]: 8460

64 Bit, Release-Modus ohne Optimierung:

  

Gemessen: GetTickCount64 () [ms]: 205
  Gemessen: Environment.TickCount [ms]: 39
  Gemessen: DateTime.UtcNow.Ticks [ms]: 127
  Gemessen: Stoppuhr: .ElapsedTicks [ms]: 209
  Gemessen: Stoppuhr: .ElapsedMilliseconds [ms]: 285
  Gemessen: statischer Stopwatch.GetTimestamp [ms]: 187
  Gemessen: Stoppuhr + Umstellung auf Datetime [ms]: 319
  Vergleichen Sie das mit DateTime.Now.Ticks [ms]: 3040

64 Bit, Release-Modus, optimiert:

Gemessen: GetTickCount64 () [ms]: 148
  Gemessen: Environment.TickCount [ms]: 31 (? Ist es noch wert)
  Gemessen: DateTime.UtcNow.Ticks [ms]: (!) 76
  Gemessen: Stoppuhr: .ElapsedTicks [ms]: 178
  Gemessen: Stoppuhr: .ElapsedMilliseconds [ms]: 226
  Gemessen: statischer Stopwatch.GetTimestamp [ms]: 175
  Gemessen: Stoppuhr + Umstellung auf Datetime [ms]: 246
  Vergleichen Sie das mit DateTime.Now.Ticks [ms]: 3020

Es kann sehr interessant sein, dass einen Datetime-Wert zu schaffen aus der Stoppuhr Zeit zu drucken, scheint fast keine Kosten zu haben . Interessant ist in einem akademischen als praktischer Art und Weise ist, dass statische Stoppuhr ist etwas schneller (wie erwartet). Einige Optimierungspunkte sind recht interessant. Zum Beispiel kann ich nicht erklären, warum Stopwatch.ElapsedMilliseconds nur mit 32-Bit so langsam im Vergleich zu es anderen Varianten, zum Beispiel der statischen. Diese und DateTime.Now mehr als verdoppeln ihre Geschwindigkeit mit 64 Bit.

Sie sehen: Nur für Millionen von Hinrichtungen, die Materie der Zeit der Stoppuhr beginnt. Wenn dies wirklich der Fall ist (aber Vorsicht Mikro-Optimierung zu früh), kann es interessant sein, dass mit GetTickCount64 (), aber vor allem mit DateTime.UtcNow , haben Sie einen 64-Bit (lang) Timer mit weniger Präzision als Stoppuhr, aber schneller, so dass Sie verwirren nicht mit dem 32-Bit „hässlich“ Environment.TickCount.

haben um

Wie erwartet, DateTime.Now ist bei weitem der langsamste von allen.

Wenn Sie es ausführen, wird der Code ruft auch Ihre aktuelle Stoppuhr Genauigkeit und mehr.

Hier ist der vollständige Benchmark-Code:

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

Sie sollten verwenden die Stoppuhr Klasse statt.

Ich verwende Environment.TickCount weil:

  1. Die Stoppuhr-Klasse ist nicht im Compact-Framework.
  2. Stoppuhr verwendet den gleichen zugrunde liegenden Timing-Mechanismus wie Tickcount, so werden die Ergebnisse nicht mehr oder weniger genau sein.
  3. Das Wrap-around-Problem mit Tickcount ist kosmisch unwahrscheinlich getroffen wird (Sie müßten Ihren Computer für 27 Tage laufen lassen und dann versuchen, eine Zeit zu messen, die nur zufällig den Wrap zu überspannen -Rund Moment), und auch wenn Sie es das Ergebnis getroffen haben eine riesige negative Zeitspanne wäre (so Art auffallen würde.)

Davon abgesehen, würde ich empfehlen, auch Stoppuhr verwenden, wenn es Ihnen zur Verfügung steht. Oder Sie könnten etwa 1 Minute dauern, und eine Stoppuhr-ähnliche Klasse schreiben, die Environment.TickCount einwickelt.

BTW, ich sehe nichts in der Stoppuhr-Dokumentation, die das Umgriff Problem mit dem zugrunde liegenden Mechanismus Timer erwähnt, so würde ich nicht überrascht, überhaupt zu finden, dass Stoppuhr aus dem gleichen Problem leidet. Aber auch hier würde ich keine Zeit damit verbringen, um sie zu kümmern.

Ich wollte wickelt es in eine Stoppuhr Klasse sagen, aber Grzenio sagte schon das Richtige, also werde ich ihm eine Steigerung geben. Eine solche Einkapselung Faktoren die Entscheidung, wie auf dem Weg ist besser, und dies in der Zeit ändern können. Ich erinnere mich, schockiert, wie teuer es kann die Zeit auf einigen Systemen bekommen, so mit einem Ort, der die beste Technik implementieren kann, kann sehr wichtig sein.

Für One-Shot-Timing, es ist noch einfacher zu schreiben

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

würde ich der kosmisch unwahrscheinlich Wraparound in Tickcount erraten noch weniger ein Problem für Stoppuhr, gegeben ist, dass das ElapsedTicks Feld eine lang ist. Auf meinem Rechner ist Stoppuhr mit hohen Auflösung, bei 2.4e9 Ticks pro Sekunde. Auch bei dieser Rate, würde es über 121 Jahre dauern die Zecken Feld überläuft. Natürlich weiß ich nicht, was unter der Decke vor sich geht, so dass von Salz mit einem Korn nehmen. Allerdings habe ich feststellen, dass die Dokumentation für die Stoppuhr nicht einmal die Wraparound Problem erwähnen, während die doc für Tickcount der Fall ist.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top