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?
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);
, 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.
, 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);
}
Système.Diagnostic.Chronomètre est conçu pour cette tâche.
Chronomètre est bien, mais boucle le travail 10^6 fois, puis diviser 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.ToString();
response.Close();
timer.Stop();
De mon blog: C # mesure du temps pour les tests de Performance (pas en anglais)
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;
}
}