Mesure de l'heure exacte pour les tests de performance [dupliquer]

Cette question a déjà une réponse ici:

Quelle est la façon la plus exacte de voir combien de temps quelque chose, par exemple un appel de méthode, a pris dans le code?

Le plus simple et le plus rapide que je suppose est ceci:

DateTime start = DateTime.Now;
{
    // Do some work
}
TimeSpan timeItTook = DateTime.Now - start;

Mais à quel point est-ce exact? Sont t-il de meilleures façons de faire?

254
demandé sur Philippe Leybaert 2009-06-09 14:37:33

8 réponses

Une meilleure façon est d'utiliser la classe Chronomètre:

using System.Diagnostics;
// ...

Stopwatch sw = new Stopwatch();

sw.Start();

// ...

sw.Stop();

Console.WriteLine("Elapsed={0}",sw.Elapsed);
474
répondu Philippe Leybaert 2013-03-24 11:05:00

, 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 Stopwatch.StartNew(). Je préfère cela à la création d'un chronomètre puis à l'appel de Start() en termes de simplicité.) Évidemment, cela entraîne le hit d'invoquer un délégué, mais dans la grande majorité des cas, cela ne sera pas pertinent. Vous écrivez alors:

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

Vous pouvez même créer une interface ITimer pour cela, avec des implémentations de StopwatchTimer, CpuTimer etc si disponible.

146
répondu Jon Skeet 2009-06-09 10:46:38

, Comme d'autres ont dit: Stopwatch devrait être le bon outil pour cela. Il peut y avoir peu d'améliorations apportées, voir ce fil spécifiquement: Benchmarking petits échantillons de code en C#, cette implémentation peut-elle être améliorée?.

J'ai vu quelques conseils utiles par Thomas Maierhofer ici

Fondamentalement, son code ressemble à:

//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 le processeur a été occupé en cours d'exécution code / processus, Comme indiqué ici cela peut refléter un scénario plus réel car aucun autre processus n'affecte la mesure. Il fait quelque chose comme:

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

Une implémentation nue et détaillée de la même chose peut être trouvée ici.

J'ai écrit une classe d'aide pour effectuer les deux d'une manière 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 du Clock est la partie délicate. Si vous souhaitez afficher le é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 la moyenne des timings de lecture en rejetant le bruit. je veux dire que je calcule l'écart de chaque timing de la moyenne réelle, puis je rejette les valeurs qui étaient plus éloignées (seulement les plus lentes) de la moyenne de déviation (appelée déviation absolue; notez que ce n'est pas l'écart-type souvent entendu), et enfin retourner la moyenne des valeurs restantes. Cela signifie, par exemple, si les valeurs chronométrées sont { 1, 2, 3, 2, 100 } (en ms ou autre), il rejette 100, et renvoie la moyenne de {[12] } qui est 2. Ou si les timings sont { 240, 220, 200, 220, 220, 270 }, il ignore 270, et renvoie la moyenne de { 240, 220, 200, 220, 220 }, qui est 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);
}
65
répondu nawfal 2017-05-23 11:55:09

Utilisez la classeChronomètre

13
répondu Mehrdad Afshari 2009-06-09 10:39:10

Système.Diagnostic.Chronomètre est conçu pour cette tâche.

12
répondu Dimi Toulakis 2009-06-09 10:40:44

Chronomètre est bien, mais boucle le travail 10^6 fois, puis diviser par 10^6. Vous obtiendrez beaucoup plus de précision.

4
répondu Mike Dunlavey 2009-06-09 21:34:07

J'utilise ceci:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(myUrl);
System.Diagnostics.Stopwatch timer = new Stopwatch();

timer.Start();

HttpWebResponse response = (HttpWebResponse)request.GetResponse();

statusCode = response.StatusCode.ToString();

response.Close();

timer.Stop();

De mon blog: C # mesure du temps pour les tests de Performance (pas en anglais)

2
répondu altansezerayan 2016-12-12 17:30:31

Oui, il y a une fonction sur le noyau Windows

[System.Runtime.InteropServices.DllImport("KERNEL32")]
private static extern bool QueryPerformanceCounter(ref long lpPerformanceCount);

[System.Runtime.InteropServices.DllImport("KERNEL32")]
private static extern bool QueryPerformanceFrequency(ref long lpFrequency);

public static float CurrentSecond
{
    get
    {
        long current = 0;
        QueryPerformanceCounter(ref current);
        long frequency = 0;
        QueryPerformanceFrequency(ref frequency);
        return (float) current / (float) frequency;
    }
}
-12
répondu Alin Vasile 2012-07-17 07:52:54