Domanda

Ho bisogno di trovare un collo di bottiglia e di misurare il tempo nel modo più accurato possibile.

Il seguente snippet 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);
È stato utile?

Soluzione

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 menzionarlo DateTime.Now spesso è un po' più lento di DateTime.UtcNow a causa del lavoro da fare con i fusi orari, Ora legale e simili.

DateTime.UtcNow ha in genere una risoluzione di 15 ms.Vedere Il post sul blog di John Chapman Di DateTime.Now precisione per un ottimo riassunto.

Curiosità interessanti:Il cronometro si riattiva DateTime.UtcNow se il tuo hardware non supporta un contatore ad alta frequenza.Puoi verificare se il cronometro utilizza l'hardware per ottenere un'elevata precisione osservando il campo statico Cronometro.IsHighResolution.

Altri suggerimenti

Se vuoi qualcosa di veloce e sporco, suggerirei invece di utilizzare 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 prendere in considerazione l'utilizzo di un profiler di terze parti come FORMICHE.

Questo articolo dice che prima di tutto bisogna confrontare tre alternative, Stopwatch, DateTime.Now E DateTime.UtcNow.

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

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

Ecco un grafico delle prestazioni.Nota il basso costo in termini di prestazioni di UtcNow rispetto alle alternative:

Enter image description here

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

Una cosa Stopwatch La cosa migliore è che fornisce misurazioni temporali con una risoluzione più elevata.Un altro è la sua natura più OO.Tuttavia, creando un wrapper OO around UtcNow non può essere difficile.

È utile inserire il codice di benchmarking in una classe/metodo di utilità.IL StopWatch non è necessario che la classe lo sia Disposed O Stopped sull'errore.Quindi, il codice più semplice da usare tempo Alcuni 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 un codice di chiamata di esempio

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

IL cronometro la funzionalità sarebbe migliore (maggiore precisione).Consiglierei anche di scaricare uno dei profiler più popolari, però (DotTrace E FORMICHE sono quelli che ho usato di più...la prova gratuita di DotTrace è perfettamente funzionante e non dà fastidio come alcune delle altre).

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.

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 esecuzioni successive.

In genere provo un metodo eseguendolo 1000 o 1000000 volte in un ciclo e ottengo dati molto più accurati rispetto a eseguirlo una volta.

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

Il modo più diretto per trovare un collo di bottiglia in un thread è farlo funzionare e, mentre sta facendo qualsiasi cosa ti faccia aspettare, fermarlo con una pausa o un tasto di interruzione.Fatelo più volte.Se il collo di bottiglia impiega il X% del tempo, X% è la probabilità di coglierlo sul fatto in ogni istantanea.

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

@Sean Chambers

Per tua informazione, la classe .NET Timer non è per la diagnostica, genera eventi a un intervallo preimpostato, 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 ha richiesto qualcosa, ma solo che è passato un certo periodo di tempo.

Il timer è esposto anche come controllo in System.Windows.Forms...puoi trovarlo nella casella degli strumenti del tuo 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 passare attraverso Utilizza Cronometro anziché DataTime per ottenere un contatore delle prestazioni accurato.

Sistema di squadra di Visual Studio ha alcune funzionalità che potrebbero aiutare a risolvere questo problema.In sostanza puoi scrivere unit test e combinarli in diversi scenari da eseguire sul tuo software come parte di uno stress o di un test di carico.Ciò può aiutare a identificare le aree del codice che influiscono maggiormente sulle prestazioni delle applicazioni.

Il gruppo Patterns and Practices di Microsoft fornisce alcune indicazioni in Linee guida sui test delle prestazioni di Visual Studio Team System.

Ho appena trovato un post nel blog di Vance Morrison sull'argomento una classe CodeTimer ha scritto che fa uso StopWatch più semplice e fa alcune cose carine sul lato.

Ho fatto pochissimo di questo tipo di controllo delle prestazioni (tendo a pensare semplicemente "questo è lento, rendilo più veloce") quindi ho praticamente sempre seguito questo.

Una ricerca su Google rivela molte risorse/articoli per il controllo delle prestazioni.

Molti menzionano l'utilizzo di pinvoke per ottenere informazioni sulle prestazioni.Molti dei materiali che studio menzionano solo l'uso di perfmon..

Modificare:

Visti i talk di StopWatch..Carino!Ho imparato qualcosa :)

Sembra un buon articolo

Il modo in cui utilizzo nei miei programmi è utilizzare la classe 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 GC.Collect per modificare l'heap gestito in uno stato noto e aggiungerò la chiamata Sleep in modo che diversi intervalli di codice possano essere facilmente separati nel profilo ETW.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top