DateTime è ora il modo migliore per misurare le prestazioni di una funzione?


474

Devo trovare un collo di bottiglia e misurare con precisione il tempo possibile.

Il seguente frammento di codice è il modo migliore per misurare le prestazioni?

DateTime startTime = DateTime.Now;

// Some execution process

DateTime endTime = DateTime.Now;
TimeSpan totalTimeTaken = endTime.Subtract(startTime);

A proposito, se non stai cercando qualcosa di veloce e sporco possono essere utilizzati contatori delle prestazioni.
Jonathan C Dickinson,

1
Se hai bisogno di maggiore precisione usa Stopwatch.GetTimestamp, altrimenti la risposta è buona.
dbasnett,

@dbasnett Puoi approfondire in una risposta?
David Basarab l'

Nell'esempio sopra, cambia l'inizio e la fine in long e assegna loro Stopwatch.GetTimestamp invece di DateTime.Now. Il tempo impiegato è (end-start) /Stopwatch.Frequency.
dbasnett,

Risposte:


649

No non lo è. Usa il cronometro (in System.Diagnostics)

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

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

Il cronometro verifica automaticamente l'esistenza di timer ad alta precisione.

Vale la pena ricordare che DateTime.Nowspesso è un po 'più lento che a DateTime.UtcNowcausa del lavoro che deve essere fatto con fusi orari, ora legale e così via.

DateTime.UtcNow in genere ha una risoluzione di 15 ms. Vedi il post sul blog di John Chapman sulla DateTime.Nowprecisione per un ottimo riassunto.

Curiosità interessanti: il cronometro si riaccende DateTime.UtcNowse l'hardware non supporta un contatore ad alta frequenza. Puoi verificare se il cronometro utilizza l'hardware per ottenere un'alta precisione guardando il campo statico Stopwatch.IsHighResolution .


3
Metterei un PerformWork (); prima del cronometro per "riscaldamento".
DiVan,

2
È inoltre necessario aggiungere la raccomandazione che, se il numero PerformWork()è molto breve, si potrebbe essere in grado di chiamarlo ripetutamente e calcolare la media del batch di chiamate. Inoltre, Stopwatchcronometra un'intera serie di chiamate anziché avviarla / arrestarla per evitare un effetto strobo che confonderà le tue misurazioni di temporizzazione.
devgeezer,

1
Il cronometro non è sicuro per i thread su multicore. Vedi stackoverflow.com/questions/6664538/… e stackoverflow.com/questions/1149485/…
Pavel Savara,

1
sw.ElapsedMilliseconds; può anche
Flappy

2
@Pavel, per essere chiari, il cronometro è raccomandato da Microsoft come la migliore soluzione (basso sovraccarico e alta precisione) sui moderni processori multicore che eseguono Windows 7 e Windows 8. msdn.microsoft.com/en-us/library/windows/ desktop / ...
Ron,

91

Se vuoi qualcosa di veloce e sporco, suggerirei invece di utilizzare il cronometro per una maggiore precisione.

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

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

In alternativa, se hai bisogno di qualcosa di un po 'più sofisticato, dovresti probabilmente considerare l'utilizzo di un profiler di terze parti come ANTS .


56

Questo articolo dice che prima di tutto devi confrontare tre alternative Stopwatch, DateTime.NowAND DateTime.UtcNow.

Mostra anche che in alcuni casi (quando non esiste un contatore delle prestazioni) Cronometro utilizza DateTime.UtcNow + qualche elaborazione aggiuntiva. Per questo motivo è ovvio che in quel caso DateTime.UtcNow è l'opzione migliore (perché altri lo usano + un po 'di elaborazione)

Tuttavia, a quanto pare, il contatore esiste quasi sempre - vedi Spiegazione sul contatore delle prestazioni ad alta risoluzione e sulla sua esistenza relativa al cronometro .NET? .

Ecco un grafico delle prestazioni. Nota quanto UtcNow ha un basso costo rispetto alle alternative:

Inserisci qui la descrizione dell'immagine

L'asse X è la dimensione dei dati del campione e l'asse Y è il tempo relativo dell'esempio.

Una cosa Stopwatchmigliore è che fornisce misurazioni del tempo a risoluzione più elevata. Un altro è la sua natura più OO. Tuttavia, creare un wrapper OO UtcNownon può essere difficile.


Il primo collegamento sembra essere rotto.
Peter Mortensen,

1
si è rotto sì .. la macchina del tempo può mostrarlo, immagino. A proposito del motivo per cui modificate "i tre", qui non credo sia necessario.
Valentin Kuzub,

18

È utile inserire il codice di benchmarking in una classe / metodo di utilità. La StopWatchclasse non deve necessariamente essere Disposedo Stoppedin errore. Quindi, il codice più semplice per cronometrare qualche azione è

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

Codice di chiamata di esempio

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

Ecco la versione del metodo di estensione

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

E codice di chiamata di esempio

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

1
Che dire della migliore granularità? Molte cose accadono in meno di un ms.
Henrik,

Restituisce quindi la proprietà Elapsed, è un TimeSpan. Ti sto solo mostrando lo schema. Divertiti a implementarlo.
Anthony Mastrean,

Ritorno Elapsed.TotalMillisecondsper una maggiore precisione. Vedere questa domanda anche stackoverflow.com/questions/8894425/...
Nawfal

16

La funzionalità del cronometro sarebbe migliore (maggiore precisione). Consiglierei anche di scaricare uno dei popolari profiler, tuttavia ( DotTrace e ANTS sono quelli che ho usato di più ... la versione di prova gratuita per DotTrace è perfettamente funzionante e non assilla come alcuni degli altri).


13

Utilizzare la classe System.Diagnostics.Stopwatch.

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

// Do some code.

sw.Stop();

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

11

Idem Cronometro, è molto meglio.

Per quanto riguarda la misurazione delle prestazioni, dovresti anche verificare se il tuo "// Some Execution Process" è un processo molto breve.

Tieni inoltre presente che la prima esecuzione del tuo "// Some Execution Process" potrebbe essere molto più lenta delle successive.

In genere collaudo un metodo eseguendolo 1000 volte o 1000000 volte in un ciclo e ottengo dati molto più accurati rispetto all'esecuzione una volta.


9

Questi sono tutti ottimi modi per misurare il tempo, ma questo è solo un modo molto indiretto per trovare colli di bottiglia.

Il modo più diretto per trovare un collo di bottiglia in un thread è farlo funzionare, e mentre sta facendo tutto ciò che ti fa aspettare, fermalo con una pausa o un tasto di interruzione. Fallo più volte. Se il collo di bottiglia impiega X% del tempo, X% è la probabilità che lo catturi nell'atto su ogni istantanea.

Ecco una spiegazione più completa di come e perché funziona


7

@ Sean Chambers

Cordiali saluti, la classe .NET Timer non è per la diagnostica, genera eventi a intervalli preimpostati, come questo (da 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);
}

Quindi questo davvero non ti aiuta a sapere quanto tempo ci è voluto qualcosa, solo che è trascorso un certo periodo di tempo.

Il timer è anche esposto come controllo in System.Windows.Forms ... lo puoi trovare nella cassetta degli attrezzi del designer in VS05 / VS08


6

Questo è il modo corretto:

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

Per ulteriori informazioni, consultare Usa cronometro anziché DataTime per ottenere un contatore delle prestazioni accurato .


6

Visual Studio Team System ha alcune funzionalità che possono aiutare con questo problema. In sostanza, puoi scrivere unit test e mescolarli in diversi scenari per eseguire il tuo software come parte di uno stress o di un test di carico. Ciò può aiutare a identificare le aree di codice che incidono maggiormente sulle prestazioni delle applicazioni.

Il gruppo Patterns and Practices di Microsoft ha alcune indicazioni nella Guida ai test delle prestazioni del sistema del team di Visual Studio .



5

Questo non è abbastanza professionale:

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

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

Una versione più affidabile è:

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

Nel mio vero codice, aggiungerò GC.Collect call per modificare l'heap gestito in uno stato noto e aggiungerò Sleep in modo che diversi intervalli di codice possano essere facilmente separati nel profilo ETW.


4

Ho fatto molto poco di questo tipo di controllo delle prestazioni (tendo a pensare "questo è lento, rendilo più veloce"), quindi sono quasi sempre andato con questo.

Un google rivela molte risorse / articoli per il controllo delle prestazioni.

Molti menzionano l'uso di Pinvoke per ottenere informazioni sulle prestazioni. Molti dei materiali che studio citano solo usando perfmon ..

Modificare:

Visto i discorsi di StopWatch .. Bello! Ho imparato qualcosa :)

Sembra un buon articolo


4

Il modo in cui uso all'interno dei miei programmi sta usando la classe StopWatch come mostrato qui.

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


// Critical lines of code

long elapsedMs = sw.Elapsed.TotalMilliseconds;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.