Quelle est la manière la plus exacte de voir combien de temps, par exemple un appel de méthode, a pris du code?
Le plus simple et le plus rapide que je suppose est le suivant:
DateTime start = DateTime.Now; { // Do some work } TimeSpan timeItTook = DateTime.Now - start;
Mais comment est-ce exact? Y a-t-il de meilleurs moyens?
Un meilleur moyen est d’utiliser la classe Chronomètre:
using System.Diagnostics; // ... Stopwatch sw = new Stopwatch(); sw.Start(); // ... sw.Stop(); Console.WriteLine("Elapsed={0}",sw.Elapsed);
Comme d’autres l’ont dit, Stopwatch
est une bonne classe à utiliser ici. Vous pouvez l’emballer 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 et à l’appel de Start()
en termes de simplicité.) Evidemment, cela entraîne le fait d’appeler un délégué, mais dans la grande majorité des cas t être pertinent. Vous écrivez alors:
TimeSpan time = StopwatchUtil.Time(() => { // Do some work });
Vous pourriez même créer une interface ITimer
avec les implémentations de StopwatchTimer,
CpuTimer
etc.
Comme d’autres l’ont dit, Stopwatch
devrait être le bon outil pour cela. Cependant, il ne peut y avoir que peu d’améliorations, voir ce sujet en particulier: Analyse comparative de petits exemples de code en C #, cette implémentation peut-elle être améliorée? .
J’ai vu quelques conseils utiles de Thomas Maierhofer ici
Fondamentalement, son code ressemble à ceci:
//prevent the JIT Comstackr 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 à Process.TotalProcessTime
pour mesurer le Process.TotalProcessTime
pendant Process.TotalProcessTime
le processeur a été occupé à exécuter le code / processus même , comme illustré 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 mise en œuvre nue et détaillée du samething peut être trouvée ici.
J'ai écrit une classe d'assistance 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 Comstackr 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(action, iterations); } static void Benchmark(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(action, iterations); } }
Il suffit d'appeler
Clock.BenchmarkTime(() => { //code }, 10000000);
ou
Clock.BenchmarkCpu(() => { //code }, 10000000);
La dernière partie de l' Clock
est la partie délicate. Si vous voulez afficher le timing final, c'est à vous de choisir quel type de timing vous voulez. J'ai écrit une méthode d'extension NormalizedMean
qui vous donne la moyenne des timings de lecture éliminant le bruit. Je veux dire que je calcule la déviation de chaque timing à partir de la moyenne réelle, puis je rejette les valeurs qui étaient plus faibles (seulement les plus lentes) de la moyenne des écarts (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, que si les valeurs temporelles sont { 1, 2, 3, 2, 100 }
(en ms ou autre), il élimine 100
et renvoie la moyenne de { 1, 2, 3, 2 }
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 }
soit 220
.
public static double NormalizedMean(this ICollection 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> Deviations(this ICollection values) { if (values.Count == 0) yield break; var avg = values.Average(); foreach (var d in values) yield return Tuple.Create(d, avg - d); }
Utilisez la classe Chronomètre
System.Diagnostics.Stopwatch est conçu pour cette tâche.
Le chronomètre est correct, mais boucle le travail 10 ^ 6 fois, puis divise par 10 ^ 6. Vous obtiendrez beaucoup plus de précision.
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.ToSsortingng(); response.Close(); timer.Stop();
De mon blog: Mesure du temps C # pour les tests de performance (pas en anglais)
Oui, il y a des fonctions sur le kernel 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; } }