Date Date.Ora il modo migliore per misurare le prestazioni di una funzione?

Devo trovare un collo di bottiglia e ho bisogno di misurare il tempo il più accuratamente ansible.

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

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 di alta precisione.

Vale la pena ricordare che DateTime.Now è spesso un po ‘più lento di DateTime.UtcNow causa del lavoro che deve essere fatto con fusi orari, DST e simili.

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

Curiosità interessante: il cronometro torna su DateTime.UtcNow se il tuo hardware non supporta un contatore ad alta frequenza. È ansible verificare se il cronometro utilizza l’hardware per ottenere un’alta precisione osservando il campo statico Cronometro . IsHighResolution .

Se vuoi qualcosa di veloce e sporco, ti suggerisco di usare il cronometro per un maggiore grado di 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 .

Questo articolo dice che prima di tutto è necessario confrontare tre alternative, Stopwatch , DateTime.Now AND DateTime.UtcNow .

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

Tuttavia, a quanto pare, il contatore esiste quasi sempre. Vedere Spiegazione relativa al contatore delle prestazioni ad alta risoluzione e alla sua esistenza correlata a .NET Cronometro? .

Ecco un grafico delle prestazioni. Si noti quanto Basso costo delle prestazioni UtcNow abbia rispetto alle alternative:

Inserisci qui la descrizione dell'immagine

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

Una cosa su cui il Stopwatch è meglio è che fornisce misurazioni del tempo di risoluzione più elevata. Un altro è la sua natura più OO. Tuttavia, la creazione di un wrapper OO attorno a UtcNow non può essere difficile.

È utile inserire il codice di benchmark in una class di utilità / metodo. La class StopWatch non deve essere Disposed o Stopped in caso di errore. Quindi, il codice più semplice al momento è un’azione

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

Esempio di codice di chiamata

 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 esempio di codice chiamante

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

La funzionalità del cronometro sarebbe migliore (maggiore precisione). Ti consiglio anche di scaricare uno dei profiler popolari, anche se ( DotTrace e ANTS sono quelli che ho usato di più … la versione di prova gratuita per DotTrace è completamente funzionale e non è un po ‘come gli altri).

Utilizzare la class System.Diagnostics.Stopwatch.

 Stopwatch sw = new Stopwatch(); sw.Start(); // Do some code. sw.Stop(); // sw.ElapsedMilliseconds = the time your "do some code" took. 

Idem cronometro, è molto meglio.

Per quanto riguarda la misurazione delle prestazioni dovresti anche verificare se il tuo “// processo di esecuzione” è un processo molto breve.

Tieni inoltre presente che la prima esecuzione del tuo “// processo di esecuzione” potrebbe essere molto più lenta delle esecuzioni successive.

In genere, eseguo il test di un metodo eseguendolo 1000 volte o 1000000 volte in un ciclo e ottengo dati molto più accurati che eseguirlo una volta.

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

Il modo più diretto per trovare un bottneck in una discussione è di farlo funzionare, e mentre sta facendo tutto ciò che ti fa aspettare, fermalo con una pausa o una chiave di break. Fallo più volte Se il tuo collo di bottiglia richiede X% di tempo, X% è la probabilità che lo catturi nell’azione su ogni istantanea.

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

@ Sean Chambers

Cordiali saluti, la class .NET Timer non è per la diagnostica, ma 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 in realtà non ti aiuta a sapere quanto tempo è durato qualcosa, solo che è passata una certa quantità di tempo.

Il timer è anche esposto come controllo in System.Windows.Forms … lo puoi trovare nella tua casella degli strumenti del designer in VS05 / VS08

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, utilizzare Cronometro anziché DataTime per ottenere un contatore delle prestazioni accurato .

Visual Studio Team System ha alcune funzionalità che possono aiutare con questo problema. In sostanza è ansible scrivere test unitari e mescolarli in diversi scenari da eseguire contro il proprio software come parte di uno stress o di un test di carico. Ciò può aiutare a identificare le aree di codice che influiscono maggiormente sulle prestazioni delle applicazioni.

Il gruppo “Patterns and Practices” di Microsoft ha alcune linee guida in Visual Studio Team .

Ho appena trovato un post nel blog di Vance Morrison su una class CodeTimer che ha scritto che rende l’uso di StopWatch più facile e ha alcune cose interessanti sul lato.

Ho fatto pochissimo di questo tipo di controllo delle prestazioni (tendo a pensare semplicemente “questo è lento, renderlo più veloce”), quindi ho praticamente sempre finito con questo.

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

Molti citano usando pinvoke per ottenere informazioni sulle prestazioni. Molti dei materiali che studio solo menzionano davvero l’uso di perfmon ..

Modificare:

Visto i discorsi di StopWatch .. Bello! Ho imparato qualcosa 🙂

Questo sembra un buon articolo

Il modo in cui utilizzo all’interno dei miei programmi sta usando la class StopWatch come mostrato qui.

 Stopwatch sw = new Stopwatch(); sw.Start(); // Critical lines of code long elapsedMs = sw.Elapsed.TotalMilliseconds; 

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 codice reale, aggiungerò la chiamata a GC.Collect per modificare l'heap gestito in uno stato noto e aggiungere la chiamata di sospensione in modo che i diversi intervalli di codice possano essere facilmente separati nel profilo ETW.