Czy DateTime.Now jest najlepszym sposobem pomiaru wydajności funkcji?

474

Muszę znaleźć wąskie gardło i precyzyjnie zmierzyć czas.

Czy poniższy fragment kodu jest najlepszym sposobem pomiaru wydajności?

DateTime startTime = DateTime.Now;

// Some execution process

DateTime endTime = DateTime.Now;
TimeSpan totalTimeTaken = endTime.Subtract(startTime);
David Basarab
źródło
Nawiasem mówiąc, jeśli nie szukasz czegoś szybkiego i brudnego licznika wydajności, możesz użyć liczników wydajności.
Jonathan C Dickinson
1
Jeśli potrzebujesz większej precyzji, użyj Stopwatch.GetTimestamp, w przeciwnym razie odpowiedź jest dobra.
dbasnett,
@dbasnett Czy możesz podać więcej szczegółów w odpowiedzi?
David Basarab,
W powyższym przykładzie zmień czas rozpoczęcia i czas zakończenia na długi i przypisz do nich Stopwatch.GetTimestamp zamiast DateTime.Now. Wymagany czas to (koniec-start) / stoper. Częstotliwość.
dbasnett 08.03.11
Zobacz także: The Case Against DateTime.Now
Matt Johnson-Pint,

Odpowiedzi:

649

Nie, nie jest. Użyj stopera (w System.Diagnostics)

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

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

Stoper automatycznie sprawdza istnienie precyzyjnych timerów.

Warto wspomnieć, że DateTime.Nowczęsto jest to nieco wolniejsze niż DateTime.UtcNowze względu na pracę, którą należy wykonać w strefach czasowych, DST i tym podobnych.

DateTime.UtcNow zazwyczaj ma rozdzielczość 15 ms. Zobacz Johna Chapmana blogu o DateTime.Nowdokładności do wielkiego podsumowania.

Ciekawe ciekawostki: Stoper wraca, DateTime.UtcNowjeśli twój sprzęt nie obsługuje licznika wysokich częstotliwości. Możesz sprawdzić, czy stoper używa sprzętu do osiągnięcia wysokiej precyzji, patrząc na pole statyczne Stopwatch.IsHighResolution .

Markus Olsson
źródło
3
Umieściłbym jeden PerformWork (); przed stoperem do „nagrzewania”.
DiVan
2
Należy również dodać zalecenie, że jeśli masz PerformWork()bardzo mało, możesz być w stanie zadzwonić do niego wielokrotnie i obliczyć średnią liczbę połączeń. Czas należy też Stopwatchmierzyć całą serią połączeń, a nie uruchamiać / zatrzymywać, aby uniknąć efektu stroboskopowego, który zmąci twoje pomiary czasu.
devgeezer
1
Stoper nie jest bezpieczny dla wielowątkowości. Zobacz stackoverflow.com/questions/6664538/… i stackoverflow.com/questions/1149485/…
Pavel Savara
1
sw.ElapsedMilliseconds; może również
Flappy
2
@Pavel, dla jasności, Stoper jest zalecany przez Microsoft jako najlepsze rozwiązanie (niskie obciążenie i wysoka precyzja) w nowoczesnych procesorach wielordzeniowych z systemem Windows 7 i Windows 8. msdn.microsoft.com/en-us/library/windows/ pulpit /…
Ron
91

Jeśli chcesz czegoś szybkiego i brudnego, sugeruję użycie stopera zamiast tego dla większej precyzji.

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

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

Alternatywnie, jeśli potrzebujesz czegoś bardziej wyrafinowanego, prawdopodobnie powinieneś rozważyć użycie zewnętrznego profilera, takiego jak ANTS .

mmcdole
źródło
56

Ten artykuł mówi, że przede wszystkim musisz porównać trzy alternatywy Stopwatch, DateTime.NowAND DateTime.UtcNow.

Pokazuje także, że w niektórych przypadkach (gdy licznik wydajności nie istnieje) Stoper używa DateTime.UtcNow + dodatkowe przetwarzanie. Z tego powodu jest oczywiste, że w takim przypadku najlepsza jest opcja DateTime.UtcNow (ponieważ inni używają jej + trochę przetwarzania)

Jak się jednak okazuje, licznik prawie zawsze istnieje - patrz Objaśnienie dotyczące licznika wydajności wysokiej rozdzielczości i jego istnienia związanego z .NET Stopwatch? .

Oto wykres wydajności. Zauważ, jak niski koszt wydajności UtcNow w porównaniu z alternatywami:

Wpisz opis zdjęcia tutaj

Oś X to przykładowy rozmiar danych, a oś Y to czas względny z przykładu.

Jedna rzecz Stopwatchjest lepsza w tym, że zapewnia pomiary czasu w wyższej rozdzielczości. Innym jest jego bardziej charakter OO. Jednak utworzenie opakowania OO UtcNownie może być trudne.

Valentin Kuzub
źródło
Pierwszy link wydaje się być uszkodzony.
Peter Mortensen
1
stał się zepsuty tak ... wehikuł czasu może to pokazać, tak sądzę. Przy okazji, dlaczego edytujesz „trzy”, tutaj chyba nie jest to potrzebne.
Valentin Kuzub,
18

Przydatne jest umieszczenie kodu testu porównawczego w klasie / metodzie użyteczności. StopWatchKlasa nie musi być Disposedalbo Stoppedw przypadku błędu. Tak, to najprostszy kod do czasu niektóre działania jest

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

Przykładowy kod wywołujący

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

Oto wersja metody rozszerzenia

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

I przykładowy kod wywołujący

public void Execute(Action action)
{
    var time = action.Benchmark()
    log.DebugFormat(“Did action in {0} ms.”, time);
}
Anthony Mastrean
źródło
1
Co z lepszą ziarnistością? Wiele rzeczy dzieje się w czasie krótszym niż jeden ms.
Henrik
Zwróć zatem właściwość Elapsed, to jest TimeSpan. Właśnie pokazuję ci wzór. Miłej zabawy przy jego wdrażaniu.
Anthony Mastrean
Wróć Elapsed.TotalMillisecondspo większą precyzję. Zobacz też to pytanie stackoverflow.com/questions/8894425/…
nawfal 12.04.2013
16

Funkcjonalność stopera byłaby lepsza (większa precyzja). Polecam również pobranie jednego z popularnych profilerów (jednak najczęściej korzystałem z DotTrace i ANTS ... bezpłatna wersja próbna DotTrace jest w pełni funkcjonalna i nie ma w tym nic dziwnego).

jsight
źródło
13

Użyj klasy System.Diagnostics.Stopwatch.

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

// Do some code.

sw.Stop();

// sw.ElapsedMilliseconds = the time your "do some code" took.
rp.
źródło
11

Ditto Stopwatch, jest o wiele lepszy.

Jeśli chodzi o pomiar wydajności, powinieneś również sprawdzić, czy Twój proces // // Some Execution Process jest bardzo krótki.

Należy również pamiętać, że pierwsze uruchomienie „// Some Execution Process” może być znacznie wolniejsze niż kolejne.

Zazwyczaj testuję metodę, uruchamiając ją 1000 razy lub 1000000 razy w pętli i uzyskuję znacznie dokładniejsze dane niż raz.

Andrei Rînea
źródło
9

Są to świetne sposoby mierzenia czasu, ale jest to tylko bardzo pośredni sposób na znalezienie wąskiego gardła (wąskich gardeł).

Najbardziej bezpośrednim sposobem na znalezienie wąskiego gardła w wątku jest uruchomienie go, a gdy robi to, co sprawia, że ​​czekasz, zatrzymaj go za pomocą klawisza pauzy lub złamania. Zrób to kilka razy. Jeśli twoje wąskie gardło zajmuje X% czasu, X% to prawdopodobieństwo, że złapiesz go na akcie na każdej migawce.

Oto pełniejsze wyjaśnienie, w jaki sposób i dlaczego to działa

Mike Dunlavey
źródło
7

@ Sean Chambers

Do Twojej wiadomości, klasa .NET Timer nie jest przeznaczona do diagnostyki, generuje zdarzenia w ustalonych odstępach czasu, takich jak ten (z 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);
}

To naprawdę nie pomaga wiedzieć, ile czasu zajęło, tylko że minęła pewna ilość czasu.

Timer jest również widoczny jako element sterujący w System.Windows.Forms ... można go znaleźć w skrzynce narzędzi projektanta w VS05 / VS08

Adam Haile
źródło
6

To jest właściwy sposób:

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

Aby uzyskać więcej informacji, przejdź do Użyj stopera zamiast DataTime w celu uzyskania dokładnego licznika wydajności .

jiya jain
źródło
6

Visual Studio Team System ma pewne funkcje, które mogą pomóc w rozwiązaniu tego problemu. Zasadniczo możesz pisać testy jednostkowe i mieszać je w różnych scenariuszach, aby uruchomić je z oprogramowaniem w ramach testu obciążenia lub obciążenia. Pomoże to zidentyfikować obszary kodu, które mają największy wpływ na wydajność aplikacji.

Grupa Microsoft ds. Wzorców i praktyk zawiera wskazówki w Visual Studio Team System Performance Testing Guidance .

Iain
źródło
5

Właśnie znalazłem post na blogu Vance'a Morrisona na temat napisanej przez niego klasy CodeTimer , która ułatwia korzystanie z niej StopWatchi robi porządne rzeczy z boku.

OwenP
źródło
5

To nie jest wystarczająco profesjonalne:

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

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

Bardziej niezawodna wersja to:

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

W moim prawdziwym kodzie dodam wywołanie GC.Collect, aby zmienić sterowaną stertę na znany stan, i dodam wywołanie uśpienia, aby różne przedziały kodu można było łatwo rozdzielić w profilu ETW.

Cześć StackOverflow
źródło
4

Zrobiłem bardzo niewiele z tego rodzaju sprawdzania wydajności (zwykle myślę, że „to jest powolne, przyspieszam”), więc prawie zawsze to robiłem.

Google ujawnia wiele zasobów / artykułów do sprawdzania wydajności.

Wielu wspomina o użyciu pinvoke w celu uzyskania informacji o wydajności. Wiele materiałów, które badam, wspomina tylko o użyciu perfmon.

Edytować:

Widziałem rozmowy StopWatch .. Fajnie! Nauczyłem się czegoś :)

To wygląda na dobry artykuł

Rob Cooper
źródło
4

Sposób, w jaki korzystam z moich programów, to użycie klasy StopWatch, jak pokazano tutaj.

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


// Critical lines of code

long elapsedMs = sw.Elapsed.TotalMilliseconds;
Blackvault
źródło