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.