老赵的原文: 一个简单的性能计数器:CodeTimer
修改说明
1. 采用 接口 取代了原代码中的 Lambda 表达式
2. 采用 GetThreadTimes 这个API 函数替代了原代码中的 QueryThreadCycleTime
这里需要说明的是 GetThreadTimes 给出了线程在内核态和用户态占用的时间,单位是 100 ns。两个时间的总和就是线程占用的CPU时间。这个API的时间精度我看了一些资料似乎没有达到 100ns. 所以GetThreadTimes 这个API函数的进度没有 QueryThreadCycleTime 高。
下面是我修改后的代码
注释1: 2009-03-11 增加委托的调用,修改 GC.Collect 参数,兼容.Net 2.0. 增加每次调用时间统计
增加了委托调用后,我发现同样是测试空函数,采用接口比采用委托效率要略高一些,这和我的预计基本吻合,因为委托不是单纯的函数调用,具体原理超出本文范围,我就不多说了。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.Threading; using System.Runtime.InteropServices; namespace ConsoleApplication3 { class Program { static void Main(string[] args) { CodeTimer.Time("Thread Sleep",1,new TestSleep3000()); CodeTimer.Time("Empty Method",1000000,new TestEmptyMethod()); CodeTimer.Time("String Concat",100000,new TestStringConcat()); CodeTimer.Time("StringBuilder Concat",100000,new TestStringBuilderConcat()); //采用委托测试 //CodeTimer.Time("Thread Sleep",1,delegate(){Thread.Sleep(3000);}); //CodeTimer.Time("Empty Method",1000000,delegate(){}); //string a=""; //CodeTimer.Time("String Concat",100000,delegate(){a+="a";}); //StringBuilder s=new StringBuilder(); //CodeTimer.Time("StringBuilder Concat",100000,delegate(){s.Append("a");}); } } 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,ActionDelegate action) { if(string.IsNullOrEmpty(name)) return; if(action==null) return; //1. Print name ConsoleColor currentForeColor = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine(name); // 2. Record the latest GC counts //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.Collect(GC.MaxGeneration); int[] gcCounts = new int[GC.MaxGeneration + 1]; for (int i = 0; i <= GC.MaxGeneration; i++) { gcCounts[i] = GC.CollectionCount(i); } // 3. Run action Stopwatch watch = new Stopwatch(); watch.Start(); long ticksFst = GetCurrentThreadTimes(); //100 nanosecond one tick for (int i = 0; i < iteration; i++) action(); long ticks = GetCurrentThreadTimes() - ticksFst; watch.Stop(); // 4. Print CPU Console.ForegroundColor = currentForeColor; Console.WriteLine("\tTime Elapsed:\t\t" + watch.ElapsedMilliseconds.ToString("N0") + "ms"); Console.WriteLine("\tTime Elapsed (one time):" + (watch.ElapsedMilliseconds / iteration).ToString("N0") + "ms"); Console.WriteLine("\tCPU time:\t\t" + (ticks * 100).ToString("N0") + "ns"); Console.WriteLine("\tCPU time (one time):\t" + (ticks * 100 / iteration).ToString("N0") + "ns"); // 5. Print GC for (int i = 0; i <= GC.MaxGeneration; i++) { int count = GC.CollectionCount(i) - gcCounts[i]; Console.WriteLine("\tGen " + i + ": \t\t\t" + count); } Console.WriteLine(); } public static void Time(string name,int iteration,IAction action) { if(string.IsNullOrEmpty(name)) return; if(action==null) return; //1. Print name ConsoleColor currentForeColor = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine(name); // 2. Record the latest GC counts //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.Collect(GC.MaxGeneration); int[] gcCounts = new int[GC.MaxGeneration + 1]; for (int i = 0; i <= GC.MaxGeneration; i++) { gcCounts[i] = GC.CollectionCount(i); } // 3. Run action Stopwatch watch = new Stopwatch(); watch.Start(); long ticksFst = GetCurrentThreadTimes(); //100 nanosecond one tick for (int i = 0; i < iteration; i++) action.Action(); long ticks = GetCurrentThreadTimes() - ticksFst; watch.Stop(); // 4. Print CPU Console.ForegroundColor = currentForeColor; Console.WriteLine("\tTime Elapsed:\t\t" + watch.ElapsedMilliseconds.ToString("N0") + "ms"); Console.WriteLine("\tTime Elapsed (one time):" + (watch.ElapsedMilliseconds / iteration).ToString("N0") + "ms"); Console.WriteLine("\tCPU time:\t\t" + (ticks * 100).ToString("N0") + "ns"); Console.WriteLine("\tCPU time (one time):\t" + (ticks * 100 / iteration).ToString("N0") + "ns"); // 5. Print GC for (int i = 0; i <= GC.MaxGeneration; i++) { int count = GC.CollectionCount(i) - gcCounts[i]; Console.WriteLine("\tGen " + i + ": \t\t\t" + count); } Console.WriteLine(); } [DllImport("kernel32.dll",SetLastError=true)] [return:MarshalAs(UnmanagedType.Bool)] static extern bool GetThreadTimes(IntPtr threadHandle,out long lpCreationTime,out long lpExitTime,out long lpKernelTime,out long lpUserTime); [DllImport("kernel32.dll")] static extern IntPtr GetCurrentThread(); public delegate void ActionDelegate(); private static long GetCurrentThreadTimes() { long l; long kernelTime,userTime; GetThreadTimes(GetCurrentThread(),out l,out l,out kernelTime,out userTime); return kernelTime+userTime; } static CodeTimer() { Process.GetCurrentProcess().PriorityClass=ProcessPriorityClass.High; Thread.CurrentThread.Priority=ThreadPriority.Highest; } } public interface IAction { void Action(); } public class TestSleep3000:IAction { public void Action() { Thread.Sleep(3000); } } public class TestEmptyMethod : IAction { public void Action() { } } public class TestStringConcat : IAction { string s=""; public void Action() { s+="a"; } } public class TestStringBuilderConcat : IAction { StringBuilder s=new StringBuilder(); public void Action() { s.Append("a"); } } }
运行结果如图: