Ist DateTime.Now der beste Weg, um die Leistung einer Funktion zu messen?

474

Ich muss einen Engpass finden und die Zeit so genau wie möglich messen.

Ist das folgende Codefragment der beste Weg, um die Leistung zu messen?

DateTime startTime = DateTime.Now;

// Some execution process

DateTime endTime = DateTime.Now;
TimeSpan totalTimeTaken = endTime.Subtract(startTime);
David Basarab
quelle
Übrigens, wenn Sie nicht auf der Suche nach etwas sind, können schnelle und schmutzige Leistungsindikatoren verwendet werden.
Jonathan C Dickinson
1
Wenn Sie eine höhere Präzision benötigen, verwenden Sie Stopwatch.GetTimestamp. Andernfalls lautet die Antwort gut.
Dbasnett
@dbasnett Kannst du in einer Antwort näher darauf eingehen?
David Basarab
Ändern Sie im obigen Beispiel Start und Endzeit in long und weisen Sie ihnen Stopwatch.GetTimestamp anstelle von DateTime.Now zu. Die benötigte Zeit ist (Ende-Start) / Stoppuhr. Frequenz.
Dbasnett
Siehe auch: Der Fall gegen DateTime.Now
Matt Johnson-Pint

Antworten:

649

Nein, ist es nicht. Benutze die Stoppuhr (in System.Diagnostics)

Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();

Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds);

Die Stoppuhr prüft automatisch, ob hochpräzise Timer vorhanden sind.

Es ist erwähnenswert, dass dies DateTime.Nowoft etwas langsamer ist als DateTime.UtcNowaufgrund der Arbeit, die mit Zeitzonen, Sommerzeit und dergleichen erledigt werden muss .

DateTime.UtcNow hat normalerweise eine Auflösung von 15 ms. Eine großartige Zusammenfassung finden Sie in John Chapmans Blogbeitrag über DateTime.NowPräzision.

Interessante Trivia: Die Stoppuhr DateTime.UtcNowgreift zurück, wenn Ihre Hardware keinen Hochfrequenzzähler unterstützt. Sie können anhand des statischen Felds Stopwatch.IsHighResolution überprüfen, ob Stopwatch Hardware verwendet, um eine hohe Präzision zu erzielen .

Markus Olsson
quelle
3
Ich würde ein PerformWork () platzieren; vor der Stoppuhr zum "Aufheizen".
DiVan
2
Es muss auch eine Empfehlung hinzugefügt werden, dass Sie, wenn Ihre PerformWork()sehr kurz ist, möglicherweise wiederholt anrufen und den Durchschnitt der Anzahl der Anrufe berechnen können. Planen Sie außerdem eine ganze Reihe von Anrufen, anstatt Sie zu starten / zu stoppen Stopwatch, um einen Strobe-Effekt zu vermeiden, der Ihre Timing-Messungen trübt.
Devgeezer
1
Stoppuhr ist bei Multicore nicht threadsicher. Siehe stackoverflow.com/questions/6664538/… und stackoverflow.com/questions/1149485/…
Pavel Savara
1
sw.ElapsedMilliseconds; kann auch
Flappy
2
@Pavel: Um es klar auszudrücken: Stopwatch wird von Microsoft als beste Lösung (geringer Overhead und hohe Präzision) für moderne Multicore-Prozessoren unter Windows 7 und Windows 8 empfohlen
Ron
91

Wenn Sie etwas schnelles und schmutziges wollen, würde ich empfehlen, stattdessen Stoppuhr zu verwenden, um ein höheres Maß an Präzision zu erzielen.

Stopwatch sw = new Stopwatch();
sw.Start();
// Do Work
sw.Stop();

Console.WriteLine("Elapsed time: {0}", sw.Elapsed.TotalMilliseconds);

Wenn Sie etwas anspruchsvolleres benötigen, sollten Sie alternativ einen Profiler eines Drittanbieters wie ANTS verwenden .

mmcdole
quelle
56

In diesem Artikel heißt es, dass Sie zunächst drei Alternativen vergleichen müssen Stopwatch, DateTime.NowUND DateTime.UtcNow.

Es zeigt auch, dass Stoppuhr in einigen Fällen (wenn kein Leistungsindikator vorhanden ist) DateTime.UtcNow + eine zusätzliche Verarbeitung verwendet. Aus diesem Grund ist es offensichtlich, dass in diesem Fall DateTime.UtcNow die beste Option ist (weil andere es verwenden + etwas Verarbeitung)

Wie sich jedoch herausstellt, ist der Zähler fast immer vorhanden - siehe Erläuterung zum hochauflösenden Leistungsindikator und seiner Existenz im Zusammenhang mit .NET-Stoppuhr? .

Hier ist ein Leistungsdiagramm. Beachten Sie, wie niedrig die Leistungskosten von UtcNow im Vergleich zu Alternativen sind:

Geben Sie hier die Bildbeschreibung ein

Die X-Achse ist die Größe der Probendaten und die Y-Achse ist die relative Zeit des Beispiels.

Eine Sache Stopwatchist besser darin, dass es Zeitmessungen mit höherer Auflösung bietet. Ein weiterer Grund ist die eher OO-Natur. Das Erstellen eines OO-Wrappers UtcNowkann jedoch nicht schwierig sein.

Valentin Kuzub
quelle
Der erste Link scheint unterbrochen zu sein.
Peter Mortensen
1
wurde kaputt yep .. Zeitmaschine kann es zeigen, würde ich raten. Übrigens, warum Sie "die drei" bearbeiten, wird das hier meiner Meinung nach nicht benötigt.
Valentin Kuzub
18

Es ist nützlich, Ihren Benchmarking-Code in eine Dienstprogrammklasse / -methode zu verschieben. Die StopWatchKlasse muss nicht Disposedoder Stoppedfehlerhaft sein. So ist die einfachste Code zu Zeit einige Aktion ist

public partial class With
{
    public static long Benchmark(Action action)
    {
        var stopwatch = Stopwatch.StartNew();
        action();
        stopwatch.Stop();
        return stopwatch.ElapsedMilliseconds;
    }
}

Beispiel für einen Anrufcode

public void Execute(Action action)
{
    var time = With.Benchmark(action);
    log.DebugFormat(“Did action in {0} ms.”, time);
}

Hier ist die Version der Erweiterungsmethode

public static class Extensions
{
    public static long Benchmark(this Action action)
    {
        return With.Benchmark(action);
    }
}

Und Beispiel für einen Anrufcode

public void Execute(Action action)
{
    var time = action.Benchmark()
    log.DebugFormat(“Did action in {0} ms.”, time);
}
Anthony Mastrean
quelle
1
Was ist mit einer besseren Granularität? Viele Dinge passieren in weniger als einer ms.
Henrik
Geben Sie dann die Eigenschaft Elapsed zurück, es ist eine Zeitspanne. Ich zeige dir nur das Muster. Viel Spaß beim Implementieren.
Anthony Mastrean
Rückkehr Elapsed.TotalMillisecondsfür eine höhere Präzision. Siehe diese Frage auch stackoverflow.com/questions/8894425/…
nawfal
16

Die Stoppuhrfunktionalität wäre besser (höhere Präzision). Ich würde jedoch auch empfehlen, nur einen der beliebten Profiler herunterzuladen ( DotTrace und ANTS sind diejenigen, die ich am häufigsten verwendet habe ... die kostenlose Testversion für DotTrace ist voll funktionsfähig und nervt nicht wie einige der anderen).

jsight
quelle
13

Verwenden Sie die System.Diagnostics.Stopwatch-Klasse.

Stopwatch sw = new Stopwatch();
sw.Start();

// Do some code.

sw.Stop();

// sw.ElapsedMilliseconds = the time your "do some code" took.
rp.
quelle
11

Das Gleiche gilt für Stoppuhr. Es ist viel besser.

In Bezug auf die Leistungsmessung sollten Sie auch prüfen, ob Ihr "// Some Execution Process" ein sehr kurzer Prozess ist.

Denken Sie auch daran, dass der erste Lauf Ihres "// Some Execution Process" möglicherweise viel langsamer ist als die nachfolgenden Läufe.

Normalerweise teste ich eine Methode, indem ich sie 1000-mal oder 1000000-mal in einer Schleife ausführe, und ich erhalte viel genauere Daten als einmal.

Andrei Rînea
quelle
9

Dies sind alles großartige Möglichkeiten, um die Zeit zu messen, aber dies ist nur eine sehr indirekte Methode, um Engpässe zu finden.

Der direkteste Weg, um einen Engpass in einem Thread zu finden, besteht darin, ihn zum Laufen zu bringen. Während er alles tut, was Sie warten lässt, halten Sie ihn mit einer Pause oder einer Unterbrechungstaste an. Mach das mehrmals. Wenn Ihr Engpass X% der Zeit in Anspruch nimmt, ist X% die Wahrscheinlichkeit, dass Sie ihn bei jedem Schnappschuss auf frischer Tat ertappen.

Hier finden Sie eine ausführlichere Erklärung, wie und warum es funktioniert

Mike Dunlavey
quelle
7

@ Sean Chambers

Zu Ihrer Information, die .NET Timer-Klasse ist nicht für die Diagnose vorgesehen. Sie generiert Ereignisse in einem voreingestellten Intervall wie diesem (von MSDN ):

System.Timers.Timer aTimer;
public static void Main()
{
    // Create a timer with a ten second interval.
    aTimer = new System.Timers.Timer(10000);

    // Hook up the Elapsed event for the timer.
    aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);

    // Set the Interval to 2 seconds (2000 milliseconds).
    aTimer.Interval = 2000;
    aTimer.Enabled = true;

    Console.WriteLine("Press the Enter key to exit the program.");
    Console.ReadLine();
}

// Specify what you want to happen when the Elapsed event is 
// raised.
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
    Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}

Das hilft dir also wirklich nicht zu wissen, wie lange etwas gedauert hat, nur dass eine gewisse Zeit vergangen ist.

Der Timer wird auch als Steuerelement in System.Windows.Forms ... angezeigt. Sie finden ihn in Ihrer Designer-Toolbox in VS05 / VS08

Adam Haile
quelle
6

Das ist der richtige Weg:

using System;
using System.Diagnostics;

class Program
{
    public static void Main()
    {
        Stopwatch stopWatch = Stopwatch.StartNew();

            // some other code

        stopWatch.Stop();

        // this not correct to get full timer resolution
        Console.WriteLine("{0} ms", stopWatch.ElapsedMilliseconds);

        // Correct way to get accurate high precision timing
        Console.WriteLine("{0} ms", stopWatch.Elapsed.TotalMilliseconds);
    }
}

Weitere Informationen finden Sie unter Verwenden Sie Stoppuhr anstelle von DataTime, um einen genauen Leistungsindikator zu erhalten .

jiya jain
quelle
6

Visual Studio Team System verfügt über einige Funktionen, die bei diesem Problem hilfreich sein können. Im Wesentlichen können Sie Komponententests schreiben und diese in verschiedenen Szenarien mischen, um sie im Rahmen eines Stress- oder Belastungstests mit Ihrer Software auszuführen. Dies kann dazu beitragen, Codebereiche zu identifizieren, die sich am stärksten auf die Leistung Ihrer Anwendungen auswirken.

Die Gruppe "Muster und Vorgehensweisen" von Microsoft enthält einige Anleitungen in den Anleitungen zum Testen der Systemleistung von Visual Studio Team .

Iain
quelle
5

Das ist nicht professionell genug:

Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();

Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds);

Eine zuverlässigere Version ist:

PerformWork();

int repeat = 1000;

Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < repeat; i++)
{
   PerformWork();
}

sw.Stop();

Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds / repeat);

In meinem realen Code füge ich einen GC.Collect-Aufruf hinzu, um den verwalteten Heap in einen bekannten Status zu ändern, und füge einen Sleep-Aufruf hinzu, damit verschiedene Codeintervalle im ETW-Profil leicht getrennt werden können.

Tschüss StackOverflow
quelle
4

Ich habe sehr wenig von dieser Art der Leistungsprüfung gemacht (ich denke eher "das ist langsam, mach es schneller"), also habe ich mich so ziemlich immer damit beschäftigt.

Ein Google enthüllt viele Ressourcen / Artikel für die Leistungsprüfung.

Viele erwähnen die Verwendung von Pinvoke, um Leistungsinformationen zu erhalten. Viele der Materialien, die ich studiere, erwähnen nur die Verwendung von Perfmon.

Bearbeiten:

Gesehen die Gespräche von StopWatch .. Schön! Ich habe etwas gelernt :)

Das sieht nach einem guten Artikel aus

Rob Cooper
quelle
4

In meinen Programmen verwende ich die hier gezeigte StopWatch-Klasse.

Stopwatch sw = new Stopwatch();
sw.Start();


// Critical lines of code

long elapsedMs = sw.Elapsed.TotalMilliseconds;
Blackvault
quelle