DateTime.Now est-il le meilleur moyen de mesurer les performances d'une fonction?


474

J'ai besoin de trouver un goulot d'étranglement et de mesurer le plus précisément possible le temps.

L'extrait de code suivant est-il le meilleur moyen de mesurer les performances?

DateTime startTime = DateTime.Now;

// Some execution process

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

Soit dit en passant, si vous ne cherchez pas quelque chose de rapide et sale, des compteurs de performance peuvent être utilisés.
Jonathan C Dickinson,

1
Si vous avez besoin d'une plus grande précision, utilisez Stopwatch.GetTimestamp, sinon la réponse est bonne.
dbasnett

@dbasnett Pouvez-vous entrer plus en détail dans une réponse?
David Basarab

Dans l'exemple ci-dessus, définissez start et endtime sur long et affectez-leur Stopwatch.GetTimestamp au lieu de DateTime.Now. Le temps nécessaire est (fin-début) / Stopwatch.Frequency.
dbasnett

Réponses:


649

Non ce n'est pas. Utilisez le chronomètre (en System.Diagnostics)

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

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

Le chronomètre vérifie automatiquement l'existence de minuteries de haute précision.

Il convient de mentionner que DateTime.Nowcela est souvent un peu plus lent qu'en DateTime.UtcNowraison du travail qui doit être fait avec les fuseaux horaires, l' heure d'été et autres.

DateTime.UtcNow a généralement une résolution de 15 ms. Voir le blog de John Chapman sur la DateTime.Nowprécision pour un excellent résumé.

Anecdote intéressante: le chronomètre retombe DateTime.UtcNowsi votre matériel ne prend pas en charge un compteur haute fréquence. Vous pouvez vérifier si Stopwatch utilise du matériel pour atteindre une haute précision en consultant le champ statique Stopwatch.IsHighResolution .


3
Je placerais un PerformWork (); avant le chronomètre pour "chauffer".
DiVan

2
Doit également ajouter la recommandation que si votre PerformWork()téléphone est très court, vous pourrez l'appeler à plusieurs reprises et calculer la moyenne du lot d'appels. Aussi, chronométrez un lot entier d'appels plutôt que de démarrer / arrêter votre Stopwatchpour éviter un effet stroboscopique qui brouillera vos mesures de synchronisation.
devgeezer

1
Le chronomètre n'est pas threadsafe sur multicœur. Voir stackoverflow.com/questions/6664538/… et stackoverflow.com/questions/1149485/…
Pavel Savara

1
sw.ElapsedMilliseconds; peut également
Flappy

2
@Pavel, pour être clair, le chronomètre est recommandé par Microsoft comme la meilleure solution (faible surcharge et haute précision) sur les processeurs multicœurs modernes qui exécutent Windows 7 et Windows 8. msdn.microsoft.com/en-us/library/windows/ desktop /…
Ron

91

Si vous voulez quelque chose de rapide et de sale, je suggère d'utiliser le chronomètre à la place pour plus de précision.

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

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

Alternativement, si vous avez besoin de quelque chose d'un peu plus sophistiqué, vous devriez probablement envisager d'utiliser un profileur tiers tel que ANTS .


56

Cet article dit que vous devez d'abord comparer trois alternatives Stopwatch, DateTime.NowET DateTime.UtcNow.

Il montre également que dans certains cas (lorsque le compteur de performances n'existe pas), le chronomètre utilise DateTime.UtcNow + un traitement supplémentaire. Pour cette raison, il est évident que dans ce cas, DateTime.UtcNow est la meilleure option (car d'autres l'utilisent + certains traitements)

Cependant, il s'avère que le compteur existe presque toujours - voir Explication sur le compteur de performances haute résolution et son existence liée au chronomètre .NET? .

Voici un graphique des performances. Notez le faible coût de performance d'UtcNow par rapport aux alternatives:

Entrez la description de l'image ici

L'axe X représente la taille des données de l'échantillon et l'axe Y représente le temps relatif de l'exemple.

Une chose Stopwatchest meilleure, c'est qu'il fournit des mesures de temps de résolution plus élevée. Un autre est sa nature plus OO. Cependant, la création d'un wrapper OO UtcNowne peut pas être difficile.


Le premier lien semble rompu.
Peter Mortensen

1
est devenu cassé yep .. machine à remonter le temps peut le montrer, je suppose. Btw pourquoi vous éditez "les trois", le n'est pas nécessaire ici je crois.
Valentin Kuzub

18

Il est utile de pousser votre code d'analyse comparative dans une classe / méthode utilitaire. La StopWatchclasse n'a pas besoin d'être Disposedou Stoppedd'erreur. Ainsi, le code le plus simple pour chronométrer une action est

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

Exemple de code d'appel

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

Voici la version de la méthode d'extension

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

Et un exemple de code d'appel

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

1
Qu'en est-il d'une meilleure granularité? Beaucoup de choses se produisent en moins d'une ms.
Henrik

Retournez ensuite la propriété Elapsed, c'est un TimeSpan. Je vous montre juste le motif. Amusez-vous à le mettre en œuvre.
Anthony Mastrean

Revenez Elapsed.TotalMillisecondspour plus de précision. Voir aussi cette question stackoverflow.com/questions/8894425/…
nawfal

16

La fonctionnalité chronomètre serait meilleure (précision plus élevée). Je recommanderais également de télécharger l'un des profileurs populaires, ( DotTrace et ANTS sont ceux que j'ai le plus utilisés ... l'essai gratuit de DotTrace est entièrement fonctionnel et ne taraude pas comme certains autres).


13

Utilisez 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 Chronomètre, c'est bien mieux.

En ce qui concerne la mesure des performances, vous devez également vérifier si votre "// certains processus d'exécution" est un processus très court.

Gardez également à l'esprit que la première exécution de votre "// Certains processus d'exécution" peut être beaucoup plus lente que les exécutions suivantes.

Je teste généralement une méthode en l'exécutant 1000 fois ou 1000000 fois dans une boucle et j'obtiens des données beaucoup plus précises qu'une seule fois.


9

Ce sont tous d'excellents moyens de mesurer le temps, mais ce n'est qu'un moyen très indirect de trouver des goulets d'étranglement.

Le moyen le plus direct de trouver un goulot d'étranglement dans un thread est de le faire fonctionner, et pendant qu'il fait tout ce qui vous fait attendre, arrêtez-le avec une touche pause ou pause. Faites cela plusieurs fois. Si votre goulot d'étranglement prend X% du temps, X% est la probabilité que vous l'attrapiez dans l'acte à chaque instantané.

Voici une explication plus complète de comment et pourquoi cela fonctionne


7

@ Sean Chambers

Pour info, la classe .NET Timer n'est pas destinée aux diagnostics, elle génère des événements à un intervalle prédéfini, comme ceci (à partir de 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);
}

Donc, cela ne vous aide vraiment pas à savoir combien de temps a pris quelque chose, juste qu'un certain temps s'est écoulé.

La minuterie est également exposée comme un contrôle dans System.Windows.Forms ... vous pouvez le trouver dans votre boîte à outils de concepteur dans VS05 / VS08


6

C'est la bonne façon:

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

Pour plus d'informations, utilisez Utiliser le chronomètre au lieu de DataTime pour obtenir un compteur de performances précis .


6

Visual Studio Team System possède certaines fonctionnalités qui peuvent aider à résoudre ce problème. Essentiellement, vous pouvez écrire des tests unitaires et les mélanger dans différents scénarios pour les exécuter contre votre logiciel dans le cadre d'un test de stress ou de charge. Cela peut aider à identifier les zones de code qui ont le plus d'impact sur les performances de vos applications.

Le groupe Patterns and Practices de Microsoft propose des conseils dans Visual Studio Team System Performance Testing Guidance .


5

Je viens de trouver un article dans le blog de Vance Morrison sur une classe CodeTimer qu'il a écrit qui facilite l'utilisation StopWatchet fait des trucs sympas sur le côté.


5

Ce n'est pas assez professionnel:

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

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

Une version plus fiable est:

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

Dans mon vrai code, j'ajouterai l'appel GC.Collect pour changer le tas géré à un état connu, et ajouter l'appel Sleep pour que les différents intervalles de code puissent être facilement séparés dans le profil ETW.


4

J'ai fait très peu de ce genre de vérification des performances (j'ai tendance à penser simplement que "c'est lent, faites-le plus vite"), donc j'ai pratiquement toujours opté pour cela.

Un google révèle beaucoup de ressources / articles pour la vérification des performances.

Beaucoup mentionnent l'utilisation de pinvoke pour obtenir des informations sur les performances. Beaucoup de matériaux que j'étudie ne mentionnent vraiment que l'utilisation de perfmon.

Éditer:

Vu les pourparlers de StopWatch .. Nice! J'ai appris quelque chose :)

Cela ressemble à un bon article


4

La façon dont j'utilise dans mes programmes utilise la classe StopWatch comme indiqué ici.

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


// Critical lines of code

long elapsedMs = sw.Elapsed.TotalMilliseconds;
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.