372 votes

Mesure du temps exact pour tester les performances

Ce qui est plus exactement la manière de voir combien de temps quelque chose, par exemple un appel de méthode, pris dans le code ?

Le plus simple et plus rapide, je suppose est la suivante :

Mais comment exactement est-ce ? Existe-t-il des moyens plus efficaces ?

710voto

Philippe Leybaert Points 62715

Une meilleure façon est d’utiliser la classe Stopwatch :

192voto

Jon Skeet Points 692016

Comme d'autres l'ont dit, Stopwatch est une bonne classe pour l'utiliser ici. Vous pouvez l'envelopper dans une méthode utile:

public static TimeSpan Time(Action action)
{
    Stopwatch stopwatch = Stopwatch.StartNew();
    action();
    stopwatch.Stop();
    return stopwatch.Elapsed;
}

(Notez l'utilisation de l' Stopwatch.StartNew(). Je préfère cela à la création d'un Chronomètre, puis en appelant Start() en termes de simplicité.) Évidemment, cela implique le hit de l'invocation d'un délégué, mais dans la grande majorité des cas qui ne seront pas pertinentes. Vous devez ensuite écrire:

TimeSpan time = StopwatchUtil.Time(() =>
{
    // Do some work
});

Vous pourriez même faire un ITimer interface pour cela, avec des implémentations d' StopwatchTimer, CpuTimer etc lorsqu'ils sont disponibles.

90voto

nawfal Points 13500

Comme les autres ont dit, Stopwatch devrait être le bon outil pour cela. Il peut y avoir quelques améliorations apportées à ce que, voir ce fil en particulier: l'analyse comparative des petits exemples de code en C#, cette mise en œuvre peut-elle être améliorée?.

J'ai vu quelques conseils utiles par Thomas Maierhofer ici

Fondamentalement, son code ressemble à ceci:

//prevent the JIT Compiler from optimizing Fkt calls away
long seed = Environment.TickCount;

//use the second Core/Processor for the test
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2);

//prevent "Normal" Processes from interrupting Threads
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;

//prevent "Normal" Threads from interrupting this thread
Thread.CurrentThread.Priority = ThreadPriority.Highest;

//warm up
method();

var stopwatch = new Stopwatch()
for (int i = 0; i < repetitions; i++)
{
    stopwatch.Reset();
    stopwatch.Start();
    for (int j = 0; j < iterations; j++)
        method();
    stopwatch.Stop();
    print stopwatch.Elapsed.TotalMilliseconds;
}

Une autre approche consiste à s'appuyer sur Process.TotalProcessTime pour mesurer combien de temps la CPU a été occupé à l'exécution de la très code/processus, comme le montre ici, Cela peut refléter plus de réel scénario étant donné qu'aucun autre processus affecte la mesure. Il fait quelque chose comme:

 var start = Process.GetCurrentProcess().TotalProcessorTime;
 method();
 var stop = Process.GetCurrentProcess().TotalProcessorTime;
 print (end - begin).TotalMilliseconds;

Un nu de la mise en œuvre détaillée de la samething peuvent être trouvés ici.

J'ai écrit une classe d'assistance pour effectuer à la fois dans un format facile à utiliser:

public class Clock
{
    interface IStopwatch
    {
        bool IsRunning { get; }
        TimeSpan Elapsed { get; }

        void Start();
        void Stop();
        void Reset();
    }



    class TimeWatch : IStopwatch
    {
        Stopwatch stopwatch = new Stopwatch();

        public TimeSpan Elapsed
        {
            get { return stopwatch.Elapsed; }
        }

        public bool IsRunning
        {
            get { return stopwatch.IsRunning; }
        }



        public TimeWatch()
        {
            if (!Stopwatch.IsHighResolution)
                throw new NotSupportedException("Your hardware doesn't support high resolution counter");

            //prevent the JIT Compiler from optimizing Fkt calls away
            long seed = Environment.TickCount;

            //use the second Core/Processor for the test
            Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2);

            //prevent "Normal" Processes from interrupting Threads
            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;

            //prevent "Normal" Threads from interrupting this thread
            Thread.CurrentThread.Priority = ThreadPriority.Highest;
        }



        public void Start()
        {
            stopwatch.Start();
        }

        public void Stop()
        {
            stopwatch.Stop();
        }

        public void Reset()
        {
            stopwatch.Reset();
        }
    }



    class CpuWatch : IStopwatch
    {
        TimeSpan startTime;
        TimeSpan endTime;
        bool isRunning;



        public TimeSpan Elapsed
        {
            get
            {
                if (IsRunning)
                    throw new NotImplementedException("Getting elapsed span while watch is running is not implemented");

                return endTime - startTime;
            }
        }

        public bool IsRunning
        {
            get { return isRunning; }
        }



        public void Start()
        {
            startTime = Process.GetCurrentProcess().TotalProcessorTime;
            isRunning = true;
        }

        public void Stop()
        {
            endTime = Process.GetCurrentProcess().TotalProcessorTime;
            isRunning = false;
        }

        public void Reset()
        {
            startTime = TimeSpan.Zero;
            endTime = TimeSpan.Zero;
        }
    }



    public static void BenchmarkTime(Action action, int iterations = 10000)
    {
        Benchmark<TimeWatch>(action, iterations);
    }

    static void Benchmark<T>(Action action, int iterations) where T : IStopwatch, new()
    {
        //clean Garbage
        GC.Collect();

        //wait for the finalizer queue to empty
        GC.WaitForPendingFinalizers();

        //clean Garbage
        GC.Collect();

        //warm up
        action();

        var stopwatch = new T();
        var timings = new double[5];
        for (int i = 0; i < timings.Length; i++)
        {
            stopwatch.Reset();
            stopwatch.Start();
            for (int j = 0; j < iterations; j++)
                action();
            stopwatch.Stop();
            timings[i] = stopwatch.Elapsed.TotalMilliseconds;
            print timings[i];
        }
        print "normalized mean: " + timings.NormalizedMean().ToString();
    }

    public static void BenchmarkCpu(Action action, int iterations = 10000)
    {
        Benchmark<CpuWatch>(action, iterations);
    }
}

Appelez simplement

Clock.BenchmarkTime(() =>
{
    //code

}, 10000000);

ou

Clock.BenchmarkCpu(() =>
{
    //code

}, 10000000);

La dernière partie de l' Clock est la partie la plus délicate. Si vous souhaitez afficher l'échéance finale, c'est à vous de choisir quelle sorte de calendrier que vous souhaitez. J'ai écrit une méthode d'extension NormalizedMean qui vous donne le moyen de le lire timings à rejeter le bruit. Je veux dire que je calculer la déviation de chaque calendrier à partir de la moyenne réelle, et puis j'ai jeter les valeurs qui a été plus loin (seulement les plus lents) à partir de la moyenne de l'écart (appelé écart absolu; à noter que ce n'est pas souvent entendu écart-type), et enfin de retour à la moyenne des valeurs restantes. Cela signifie, par exemple, si chronométré valeurs sont { 1, 2, 3, 2, 100 } (en ms ou quoi que ce soit), il ignore 100, et renvoie la moyenne des { 1, 2, 3, 2 } qui 2. Ou si les timings sont { 240, 220, 200, 220, 220, 270 }, il ignore 270, et renvoie la moyenne des { 240, 220, 200, 220, 220 } qui 220.

public static double NormalizedMean(this ICollection<double> values)
{
    if (values.Count == 0)
        return double.NaN;

    var deviations = values.Deviations().ToArray();
    var meanDeviation = deviations.Sum(t => Math.Abs(t.Item2)) / values.Count;
    return deviations.Where(t => t.Item2 > 0 || Math.Abs(t.Item2) <= meanDeviation).Average(t => t.Item1);
}

public static IEnumerable<Tuple<double, double>> Deviations(this ICollection<double> values)
{
    if (values.Count == 0)
        yield break;

    var avg = values.Average();
    foreach (var d in values)
        yield return Tuple.Create(d, avg - d);
}

16voto

Mehrdad Afshari Points 204872

Utilisez la classe Stopwatch

13voto

Dimi Toulakis Points 3002

System.Diagnostics.Stopwatch est conçu pour cette tâche.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X