Exact time measurement for performance testing
查看某些内容(例如方法调用)接受代码的最精确方式是什么?
我猜的最容易和最快的是:
1 2 3 4 5 | DateTime start = DateTime.Now; { // Do some work } TimeSpan timeItTook = DateTime.Now - start; |
但这有多精确? 还有更好的方法吗?
更好的方法是使用秒表类:
1 2 3 4 5 6 7 8 9 10 11 12 | using System.Diagnostics; // ... Stopwatch sw = new Stopwatch(); sw.Start(); // ... sw.Stop(); Console.WriteLine("Elapsed={0}",sw.Elapsed); |
正如其他人所说的那样,
1 2 3 4 5 6 7 | public static TimeSpan Time(Action action) { Stopwatch stopwatch = Stopwatch.StartNew(); action(); stopwatch.Stop(); return stopwatch.Elapsed; } |
(注意使用
1 2 3 4 | TimeSpan time = StopwatchUtil.Time(() => { // Do some work }); |
您甚至可以为此创建一个
正如其他人所说,
我在这里看到了Thomas Maierhofer的一些有用的提示
基本上他的代码看起来像:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //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; } |
另一种方法是依靠
1 2 3 4 | var start = Process.GetCurrentProcess().TotalProcessorTime; method(); var stop = Process.GetCurrentProcess().TotalProcessorTime; print (end - begin).TotalMilliseconds; |
在这里可以找到相同的裸体,详细的实现。
我编写了一个帮助程序类,以易于使用的方式执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | 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); } } |
打电话吧
1 2 3 4 5 | Clock.BenchmarkTime(() => { //code }, 10000000); |
要么
1 2 3 4 5 | Clock.BenchmarkCpu(() => { //code }, 10000000); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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专为此任务而设计。
秒表很好,但循环工作10 ^ 6次,然后除以10 ^ 6。
你会得到更多的精确度。
我正在使用这个:
1 2 3 4 5 6 7 8 9 10 11 12 | 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#性能测试时间测量(不是英文)
是的,Windows内核上有一些功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | [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; } } |