精确的时间测量性能testing

什么是最确切的方式来看一些东西,例如方法调用,代码?

最容易和最快的我猜想是这样的:

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

但是这个确实如此呢? 有更好的方法吗?

更好的方法是使用秒表类:

 using System.Diagnostics; // ... Stopwatch sw = new Stopwatch(); sw.Start(); // ... sw.Stop(); Console.WriteLine("Elapsed={0}",sw.Elapsed); 

正如其他人所说, Stopwatch是一个很好的类在这里使用。 你可以用一个有用的方法来包装它:

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

(注意使用Stopwatch.StartNew() ,我更喜欢创build一个秒表,然后以简单的方式调用Start() 。)显然这招致了调用委托的命中,但在绝大多数情况下,不相关。 你会写:

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

你甚至可以为此设置一个ITimer接口,可以使用StopwatchTimer, CpuTimer等实现。

正如其他人所说, Stopwatch应该是正确的工具。 虽然可以做一些改进,但是请特别参阅这个主题: 在C#中对小代码样本进行基准testing,这个实现可以改进吗? 。

我在这里看到了Thomas Maierhofer的一些有用的提示

基本上他的代码如下所示:

 //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; } 

另一种方法是依靠Process.TotalProcessTime来测量CPU忙于运行该代码/进程的时间如下所示,这可以反映更真实的场景,因为没有其他进程影响测量。 它做类似的事情:

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

在这里可以find相同的裸体,详细的实现。

我写了一个辅助类来以易于使用的方式执行这两个操作:

 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); } } 

打电话

 Clock.BenchmarkTime(() => { //code }, 10000000); 

要么

 Clock.BenchmarkCpu(() => { //code }, 10000000); 

Clock的最后部分是棘手的部分。 如果你想显示最后的时间,那么你可以select你想要的时间。 我写了一个扩展方法NormalizedMean ,它给你读取时间的均值, 丢弃噪声。 我的意思是我计算每个时间点与实际平均值的偏差,然后从偏差平均值 (称为绝对偏差;注意它不是经常听到的标准偏差)中丢弃更加有用的值(只有较慢的值) ,最后返回剩余值的平均值。 这意味着,例如,如果定时值是{ 1, 2, 3, 2, 100 } 1,2,3,2,100 { 1, 2, 3, 2, 100 } (毫秒或其他),则丢弃100 ,并返回2的平均值{ 1, 2, 3, 2 } 。 或者如果时间是{ 240, 220, 200, 220, 220, 270 } ,则丢弃270 ,并且返回{ 240, 220, 200, 220, 220 }{ 240, 220, 200, 220, 220 } { 240, 220, 200, 220, 220, 270 }的平均值。

 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); } 

使用秒表类

System.Diagnostics.Stopwatch是为此任务而devise的。

秒表不错,可循环10 ^ 6次,再除以10 ^ 6。 你会得到更多的精度。

我正在使用这个:

 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(); 

从我的博客: C#时间测量性能testing (不是英文)

是的,Windows内核上有一些function

 [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; } }