Comment mesurer la performance du code in.NET Je ne sais pas.

je fais un benchmarking très rapide et sale sur une seule ligne de code C en utilisant DateTime:

long lStart = DateTime.Now.Ticks;
// do something
long lFinish = DateTime.Now.Ticks;

Le problème est dans les résultats:

Start Time [633679466564559902]
Finish Time [633679466564559902]

Start Time [633679466564569917]
Finish Time [633679466564569917]

Start Time [633679466564579932]
Finish Time [633679466564579932]

...et ainsi de suite.

étant donné que les heures de départ et d'arrivée sont identiques, Ticks n'est évidemment pas assez granulaire.

alors, comment puis-je mieux mesurer le rendement?

43
demandé sur Airsource Ltd 2009-01-19 16:44:27

18 réponses

la classe Stopwatch , disponible depuis .NET 2.0, est la meilleure solution. Il s'agit d'un compteur de très haute performance précis à des fractions de milliseconde. Jetez un oeil à la MSDN documentation , qui est assez clair.

modifier: comme précédemment suggéré, il est également conseillé d'exécuter votre code un certain nombre de fois afin d'obtenir un temps moyen raisonnable.

60
répondu Noldorin 2013-05-30 09:50:58

exécutez votre code à plusieurs reprises. Le problème semble être que votre code s'exécute beaucoup plus rapidement que la granularité de votre instrument de mesure. La solution la plus simple est d'exécuter votre code plusieurs, plusieurs fois (des milliers, peut-être des millions) et ensuite de calculer le temps moyen d'exécution.

éditer: aussi, en raison de la nature des compilateurs actuels d'optimisation (et des Machines virtuelles telles que le CLR et le JVM), il peut être très trompeur de mesurer la vitesse d'exécution des lignes simples de code, puisque la mesure peut influencer la vitesse beaucoup. Une bien meilleure approche consisterait à dresser le profil de l'ensemble du système (ou au moins des blocs plus grands) et à vérifier où se trouvent les goulets d'étranglement.

11
répondu Joachim Sauer 2009-01-19 13:46:47

je trouve que ces utiles

http://accelero.codeplex.com/SourceControl/changeset/view/22633#290971 http://accelero.codeplex.com/SourceControl/changeset/view/22633#290973 http://accelero.codeplex.com/SourceControl/changeset/view/22633#290972

TickTimer est une copie coupée du chronomètre qui démarre lors de la construction et ne supporte pas le redémarrage. Il informez-vous également si le matériel actuel ne supporte pas le chronométrage à haute résolution (le chronomètre avale ce problème)

Si ce

var tickTimer = new TickTimer();
//call a method that takes some time
DoStuff();
tickTimer.Stop();
Debug.WriteLine("Elapsed HighResElapsedTicks " + tickTimer.HighResElapsedTicks);
Debug.WriteLine("Elapsed DateTimeElapsedTicks " + tickTimer.DateTimeElapsedTicks);
Debug.WriteLine("Elapsed ElapsedMilliseconds " + tickTimer.ElapsedMilliseconds);
Debug.WriteLine("Start Time " + new DateTime(tickTimer.DateTimeUtcStartTicks).ToLocalTime().ToLongTimeString());

affichera ce

Elapsed HighResElapsedTicks 10022886
Elapsed DateTimeElapsedTicks 41896
Elapsed ElapsedMilliseconds 4.18966178849554
Start Time 11:44:58

DebugTimer est un wrapper pour TickTimer qui écrira le résultat à déboguer. (note: it supports the jetable pattern)

Si ce

using (new DebugTimer("DoStuff"))
{
    //call a method that takes some time
    DoStuff();
}

affichera ceci dans la fenêtre de débogage

DoStuff: Total 3.6299 ms

IterationDebugTimer est pour chronométrer combien de temps il faut pour exécuter une opération plusieurs fois et écrire le résultat pour déboguer. Il effectuera également un lancement initial qui n'est pas inclus afin d'ignorer le temps de démarrage. (note: it supports the jetable pattern)

Si ce

int x;
using (var iterationDebugTimer = new IterationDebugTimer("Add", 100000))
{
    iterationDebugTimer.Run(() =>
    {
        x = 1+4;
    });
}

affichera ce

Add: Iterations 100000 
Total 1.198540 ms 
Single 0.000012 ms
8
répondu Simon 2009-02-27 01:01:19

juste pour ajouter à ce que d'autres ont déjà dit à propos de L'utilisation du chronomètre et de la mesure des moyennes.

assurez-vous d'appeler votre méthode avant de mesurer. Sinon, vous mesurerez le temps nécessaire pour compiler le code JIT. Cela pourrait fausser vos chiffres de manière significative.

aussi, assurez-vous de mesurer le code de mode de publication car les optimisations sont désactivées par défaut pour les constructions de débogage. Accorder le code de débogage est inutile imho.

Et assurez-vous de mesurer ce que vous voulez réellement mesurer. Lorsque les optimisations se déclenchent, le compilateur / JIT peut réorganiser le code ou le supprimer entièrement, de sorte que vous pouvez finir par mesurer quelque chose d'un peu différent de ce qui est prévu. Au moins, jetez un coup d'oeil au code généré pour vous assurer que le code n'a pas été dépouillé.

en Fonction de ce que vous essayez de mesurer gardez à l'esprit, qu'un réel système mettra l'accent sur l'exécution différemment qu'une application de test. Quelque les problèmes de performance sont liés, par exemple, à la façon dont les objets sont ramassés. Ces problèmes n'apparaissent généralement pas dans une simple application de test.

en fait, le meilleur conseil est de mesurer des systèmes réels avec des données réelles car les tests de bac à sable peuvent s'avérer très imprécis.

8
répondu Brian Rasmussen 2016-02-18 18:52:09

utilisez un vrai profileur comme dotTrace.

4
répondu Otávio Décio 2009-01-19 13:47:15

vous pouvez utiliser le Stopwatch , en supposant que vous utilisez .NET 2.0 ou plus récent.

System.Diagnostics.Stopwatch.StartNew();

la classe Stopwatch a aussi le champ public en lecture seule IsHighResolution qui vous indiquera si le chronomètre est basé sur un compteur de performance à haute résolution. Si ce n'est pas le cas, il est basé sur la minuterie du système.

Je ne sais pas ce qu'il faut pour que le chronomètre soit basé sur un compteur de performance à haute résolution. Il y a quelques Appels API mais je me dis que si le chronomètre n'utilise pas une haute résolution, alors L'API n'est probablement pas là.

4
répondu JoshBerke 2016-04-28 11:12:40

voir la réponse à est DateTime.Maintenant, la meilleure façon de mesurer le rendement d'une fonction? pour une explication ou lire mon post de blog sur la mesure de la performance

le problème est que DateTime a une résolution d'environ 15ms, il ne peut pas être plus précis que cela. Chronomètre, cependant, peut.

3
répondu Markus Olsson 2017-05-23 12:16:53
3
répondu Patrick Cuff 2009-01-19 14:24:03

échantillon pour Stopwatch classe

    using System.Diagnostics;
    ......
    ...
    ..
    Stopwatch sw = new Stopwatch();
    sw.Start();
    //Your Code Here

    sw.Stop();
    Console.WriteLine("Elapsed={0}",sw.Elapsed);
3
répondu WhTiggaW 2014-08-08 16:41:30

https://andreyakinshin.gitbooks.io/performancebookdotnet/content/science/microbenchmarking.html

https://github.com/PerfDotNet/BenchmarkDotNet

" en effet, microbencmarking est très difficile. Si une opération prend 10-100ns, la mesure de l'opération est un grand défi. Je vous suggère D'utiliser BenchmarkDotNet pour vos benchmarks. C'est une bibliothèque qui peut vous aider à faire un honnête référence et d'obtenir des mesures avec une bonne précision. Bien sûr, vous pouvez écrire de référence, sans autres bibliothèques. Dans cette section, nous expliquons pourquoi il est probablement une mauvaise idée et ce que vous devriez savoir avant de commencer."

3
répondu Evgeniy 2015-12-15 13:11:04

cet article de projet de code montre comment utiliser le minuteur haute performance pour mesurer la vitesse d'exécution de votre code:

http://www.codeproject.com/KB/cs/highperformancetimercshar.aspx

Ici vous pouvez trouver un certain nombre de l'open source C# profileurs:

http://csharp-source.net/open-source/profilers

1
répondu Ashley Davis 2010-05-11 10:58:29

une autre option consiste à insérer automatiquement le code de minuterie avec Fody . Cela rend votre code beaucoup plus facile à lire car il sépare vos préoccupations transversales. Je pense que c'est proche de ce qu'on appelle Programmation Orientée Vers L'Aspect , mais fait au moment de la post-compilation.

voir https://github.com/Fody/MethodTimer pour l'addon de fody qui fait chronométrage de méthode.

citant du Readme:

avec un intercepteur, quelque part dans votre assemblage:

public static class MethodTimeLogger {
  public static void Log(MethodBase methodBase, long milliseconds)
  {
    //Do some logging here
  } 
}

votre code,

public class MyClass
{
    [Time]
    public void MyMethod()
    {
        //Some code u are curious how long it takes
        Console.WriteLine("Hello");
    }
}

est compilé pour ceci:

public class MyClass
{
    public void MyMethod()
    {
        var stopwatch = Stopwatch.StartNew();
        try
        {
            //Some code u are curious how long it takes
            Console.WriteLine("Hello");
        }
        finally
        {
            stopwatch.Stop();
            MethodTimeLogger.Log(methodof(MyClass.MyMethod), stopwatch.ElapsedMilliseconds);
        }
    }
}
1
répondu Meirion Hughes 2016-02-18 19:12:15

Jetable style Stopwatch qui fonctionne le mieux pour moi.

class VWatch : IDisposable {
    Stopwatch watch = new Stopwatch();
    public VWatch() {
        this.watch.Start();
    }
    public void Dispose() {
        this.watch.Stop();
        Console.WriteLine("Finished. Elapsed={0}", this.watch.Elapsed);
    }
}

et ensuite:

using (new VWatch()) {
    /// do something for time measurement
}
1
répondu Val 2018-07-24 11:49:18

parfois, il peut être préférable de regarder pourquoi vous avez besoin de chronométrer l'opération? Est-il lent? Ou vous êtes simplement curieux? Première règle de l'optimisation est "ne fais pas cela". Ainsi, selon ce que vous mesurez réellement, pourrait changer l'opinion sur ce qui est le mieux adapté à la tâche.

0
répondu 2009-01-19 20:39:21

plus facile à utiliser un profileur comme profileur de Performance de fourmis , ou l'un des autres qui sont disponibles.

0
répondu Ben Emmett 2012-07-27 14:12:55

j'ai fait une extension qui renvoie des millisecondes de tiques.

public static int GetTotalRunningTimeInMilliseconds(this DateTime start)
{
    var endTicks = DateTime.Now.Ticks - start.Ticks;
    return TimeSpan.FromTicks(endTicks).Milliseconds;
}

Utilisation:

 var start = DateTime.Now;

 //...your long running code here

 var endTime = start.GetTotalRunningTimeInMilliseconds();
0
répondu Amc_rtty 2013-08-14 12:31:41

j'ai fait une méthode très simple qui mesure la vitesse d'exécution d'une Action , qui a pour moi l'avantage que je peux la réutiliser chaque fois que j'en ai besoin, et quel que soit le code que je dois mesurer.

pour moi un DateTime était suffisant, mais il est facilement adaptable de DateTime à Chronomètre .

public static TimeSpan MeasureTime(Action action)
{
    DateTime start = DateTime.Now;

    if (action == null)
    {
        throw new ArgumentNullException("action");
    }

    try
    {
        action();
    }
    catch (Exception ex)
    {
        Debugger.Log(1, "Measuring",ex.ToString());
    }

    return DateTime.Now - start;
}

Comment l'utiliser?:

private static void StressTest()
{
    List<TimeSpan> tss = new List<TimeSpan>();

    for (int i = 0; i < 100; i++)
    {
        // here is the measuring:
        var ts = MeasureTime(() => instance.Method("param1"));

        tss.Add(ts);
    }

    Console.WriteLine("Max: {0}", tss.Max());
    Console.WriteLine("Min: {0}", tss.Min());
    Console.WriteLine("Avg: {0}", TimeSpan.FromMilliseconds(tss.Average(i => i.TotalMilliseconds)));
}

ou:

var ts = MeasureTime(() =>
                        {
                            // Some intensive stuff here
                            int a = 1;

                            // more here
                            int b = 2;

                            // and so on
                        });
0
répondu coloboxp 2017-12-14 11:11:27

pour mesurer la performance avec différence entre les mesures, j'utilise cette classe. La classe Chronomètre n'a pas la méthode Split .

/// <summary>
/// Stopwatch like class that keeps track of timelapses.
/// Probably low-res because of the usage of DateTime.
/// </summary>
public class ChronoMeter
{
    /// <summary>
    /// The name given when the Chronometer was constructed.
    /// </summary>
    public string Name { get; private set; }
    /// <summary>
    /// The moment in time Start was called.
    /// </summary>
    public DateTime Started { get; private set; }

    /// <summary>
    /// All time recordings are added to this list by calling Split and Stop.
    /// </summary>
    public List<ChronoRecord> Records { get; private set; }

    private readonly Stopwatch _stopWatch = new Stopwatch();

    private bool _hasBeenStopped = false;

    /// <summary>
    /// Constrcutor
    /// </summary>
    /// <param name="pName">The name is used in logging</param>
    /// <param name="pLoggingType">The type of logging appriate for the information yielded by this time recording.</param>
    public ChronoMeter(string pName)
    {
        Name = pName;
        Records = new List<ChronoRecord>();
    }

    /// <summary>
    /// Not calling Stop is bad practise. Therefore a little safety net zo the end is still recorderd.
    /// Keep in mind that the garbase collector invokes the destructor, so the moment of time probably doesn't make much sense.
    /// It is more to notify that you should have used Stop for the latest split.
    /// </summary>
    ~ChronoMeter()
    {
        if (!_hasBeenStopped)
        {
            Stop("Destructor safety net");
        }
    }

    /// <summary>
    /// TimeElapsedSinceStart of a ChronoRecord is relative to the moment ChronoMeter was started by calling this function.
    /// </summary>
    public void Start()
    {
        _stopWatch.Start();
        Started = DateTime.Now;
    }

    /// <summary>
    /// Splits the timerecording and add a record of this moment to the list of split records.
    /// </summary>
    /// <param name="pSplitName"></param>
    public void Split(string pSplitName)
    {
        _stopWatch.Stop();
        var created = Started + _stopWatch.Elapsed;
        var previousRecord = Records.LastOrDefault();
        Records.Add(new ChronoRecord(pSplitName, Started, created, previousRecord));
        _stopWatch.Start();
    }

    /// <summary>
    /// Indicates you are done and the records will be written to the log.
    /// </summary>
    public void Stop(string pSplitName)
    {
        Split(pSplitName);
        _stopWatch.Stop();
        _hasBeenStopped = true;
    }

    public class ChronoRecord
    {
        public string Name { get; private set; }
        public TimeSpan TimeElapsedSinceStart { get; private set; }
        public TimeSpan TimeElapsedSincePrevious { get; private set; }
        public DateTime Start { get; private set; }
        public DateTime Created { get; private set; }

        public ChronoRecord(string pName, DateTime pStartDateTime, DateTime pCreated, ChronoRecord pPreviousRecord=null)
        {
            if (pCreated == default(DateTime)) //Ignore DefaultDateTimeComparison
            {
                pCreated = DateTime.Now;
            }
            Created = pCreated;
            Name = pName;
            Start = pStartDateTime;

            TimeElapsedSinceStart = Created - Start;
            if (pPreviousRecord != null)
            {
                TimeElapsedSincePrevious = Created - pPreviousRecord.Created;
            }
            else
            {
                TimeElapsedSincePrevious = TimeElapsedSinceStart;
            }
        }
    }
}
-1
répondu Mike de Klerk 2015-09-10 06:48:08