方法性能比较的好帮手:CodeTimer的扩展应用
在09年的.NET技术大会上Jeffery Richard展示的性能测试代码中,功能强大的CodeTimer引起了大家的注意,由于Jeffery Richard还没有公布源码,于是园子里的老赵就自己写了一个,适用于在Console应用程序中输出性能测试的各个指标。
最近参与的项目中有很多想要进行方法性能比较的地方,于是就想起了这个CodeTimer,便狗尾续貂在老赵原有的代码上加上了一个Time方法的重载,以适用于非Console应用程序中快速的性能测试,比如Winform、Web、UnitTest、WindowsService等等。
修改后的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using System.Diagnostics; using System.Threading; namespace PerformanceTest { public static class CodeTimer { public static void Initialize() { Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High; Thread.CurrentThread.Priority = ThreadPriority.Highest; Time("", 1, () => { }); } public static void Time(string name, int iteration, Action action) { Time(name, iteration, action, Console.WriteLine); } public static void Time(string name, int iteration, Action action, Action<string> output) { if (String.IsNullOrEmpty(name)) return; // 1. ConsoleColor currentForeColor = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Yellow; output(name); // 2. GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); int[] gcCounts = new int[GC.MaxGeneration + 1]; for (int i = 0; i <= GC.MaxGeneration; i++) { gcCounts[i] = GC.CollectionCount(i); } // 3. Stopwatch watch = new Stopwatch(); watch.Start(); ulong cycleCount = GetCycleCount(); for (int i = 0; i < iteration; i++) action(); ulong cpuCycles = GetCycleCount() - cycleCount; watch.Stop(); // 4. Console.ForegroundColor = currentForeColor; output("\tTime Elapsed:\t" + watch.ElapsedMilliseconds.ToString("N0") + "ms"); output("\tCPU Cycles:\t" + cpuCycles.ToString("N0")); // 5. for (int i = 0; i <= GC.MaxGeneration; i++) { int count = GC.CollectionCount(i) - gcCounts[i]; output("\tGen " + i + ": \t\t" + count); } output(string.Empty); } private static ulong GetCycleCount() { ulong cycleCount = 0; QueryThreadCycleTime(GetCurrentThread(), ref cycleCount); return cycleCount; } [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool QueryThreadCycleTime(IntPtr threadHandle, ref ulong cycleTime); [DllImport("kernel32.dll")] static extern IntPtr GetCurrentThread(); } }
这样一来,就能够在Winform、Web、UnitTest、Service等各种应用程序中方便快捷的使用CodeTimer了,比如在Winform中的一个应用:
StringBuilder result; private void button1_Click(object sender, EventArgs e) { string s = string.Empty; PerformanceTest.CodeTimer.Time("String Concat", 10000, () => { s += "a"; }, Print); StringBuilder sb = new StringBuilder(); PerformanceTest.CodeTimer.Time("StringBuilder", 10000, () => { sb.Append("a"); }, Print); this.textBox1.Text = result.ToString(); } void Print(string msg) { if (result == null) result = new StringBuilder(); result.AppendLine(msg); Console.WriteLine(msg); }