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

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

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 .


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 .


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.


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

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


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.

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.


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


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


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 .


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 .



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.


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ł


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;
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.