Wie andere sagten, Stopwatch
sollte das richtige Werkzeug dafür sein. Es können jedoch noch einige Verbesserungen vorgenommen werden, siehe insbesondere diesen Thread: Benchmarking kleiner Codebeispiele in C#, kann diese Implementierung verbessert werden? .
Ich habe einige nützliche Tipps gesehen von Thomas Maierhofer hier
Sein Code sieht im Wesentlichen so aus:
//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;
}
Ein anderer Ansatz besteht darin, sich auf Process.TotalProcessTime
um zu messen, wie lange die CPU beschäftigt war Ausführen des Codes/Prozesses selbst , wie hier gezeigt Dies kann ein realistischeres Szenario widerspiegeln, da kein anderer Prozess die Messung beeinflusst. Es tut so etwas wie:
var start = Process.GetCurrentProcess().TotalProcessorTime;
method();
var stop = Process.GetCurrentProcess().TotalProcessorTime;
print (end - begin).TotalMilliseconds;
Eine nackte, detaillierte Umsetzung der kann hier gefunden werden.
Ich habe eine Hilfsklasse geschrieben, die beides auf einfache Art und Weise ermöglicht:
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);
}
}
Einfach anrufen
Clock.BenchmarkTime(() =>
{
//code
}, 10000000);
o
Clock.BenchmarkCpu(() =>
{
//code
}, 10000000);
Der letzte Teil der Clock
ist der schwierige Teil. Wenn Sie das endgültige Timing anzeigen wollen, müssen Sie selbst entscheiden, welche Art von Timing Sie wollen. Ich habe eine Erweiterungsmethode geschrieben NormalizedMean
der den Mittelwert der Lesezeiten angibt den Lärm zu verwerfen. Ich meine, ich berechne die Abweichung jeder Zeitmessung vom tatsächlichen Mittelwert, und dann verwerfe ich die Werte, die weiter (nur die langsameren) vom Mittelwert entfernt sind. Mittelwert der Abweichung (genannt absolute Abweichung; beachten Sie, dass es sich dabei nicht um die oft gehörte Standardabweichung handelt), und schließlich den Mittelwert der verbleibenden Werte zurückgeben. Das bedeutet, dass zum Beispiel, wenn die zeitlichen Werte { 1, 2, 3, 2, 100 }
(in ms oder was auch immer), verwirft es 100
und gibt den Mittelwert von { 1, 2, 3, 2 }
das ist 2
. Oder wenn die Zeitangaben { 240, 220, 200, 220, 220, 270 }
verwirft sie 270
und gibt den Mittelwert von { 240, 220, 200, 220, 220 }
das ist 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);
}