对老赵写的简单性能计数器的修改
早上看到老赵写的这个性能计数器,感觉很实用,不过老赵用了很多.C# 3.0 的新语法,还用了 VISTA 和 Server 2008 下特有的Win32 API,对于还在用C#2.0 或者还工作在 XP 或者 Server 2003 下的兄弟们,只能望代码心叹了。应老赵的要求,我修改了他的代码,增加了对低版本C# 和 低版本windows 操作系统的支持。
老赵的原文: 一个简单的性能计数器: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.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
public interface IAction
{
void Action();
}
public static class CodeTimer
{
[DllImport( " kernel32.dll " , SetLastError = true )]
static extern bool GetThreadTimes(IntPtr hThread, 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, userTimer;
GetThreadTimes(GetCurrentThread(), out l, out l, out kernelTime,
out userTimer);
return kernelTime + userTimer;
}
static CodeTimer()
{
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
Thread.CurrentThread.Priority = ThreadPriority.Highest;
}
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();
}
}
测试类
{
#region IAction Members
public void Action()
{
Thread.Sleep( 3000 );
}
#endregion
}
public class TestEmptyMethod : IAction
{
#region IAction Members
public void Action()
{
}
#endregion
}
public class TestStringConcat : IAction
{
string s = "" ;
#region IAction Members
public void Action()
{
s += " a " ;
}
#endregion
}
public class TestStringBuilderConcat : IAction
{
StringBuilder s = new StringBuilder();
#region IAction Members
public void Action()
{
s.Append ( " a " );
}
#endregion
}
测试代码
采用接口
CodeTimer.Time( " Thread Sleep " , 10000000 , new TestEmptyMethod());
CodeTimer.Time( " String Concat " , 100000 , new TestStringConcat());
CodeTimer.Time( " StringBuilder Conca " , 100000 ,
new TestStringBuilderConcat());
测试结果
Thread Sleep
Time Elapsed: 2,997ms
Time Elapsed (one time):2,997ms
CPU time: 0ns
CPU time (one time): 0ns
Gen 0: 0
Gen 1: 0
Gen 2: 0
Empty Method
Time Elapsed: 138ms
Time Elapsed (one time):0ms
CPU time: 125,000,000ns
CPU time (one time): 12ns
Gen 0: 0
Gen 1: 0
Gen 2: 0
String Concat
Time Elapsed: 10,547ms
Time Elapsed (one time):0ms
CPU time: 10,546,875,000ns
CPU time (one time): 105,468ns
Gen 0: 4102
Gen 1: 2661
Gen 2: 2545
StringBuilder Conca
Time Elapsed: 4ms
Time Elapsed (one time):0ms
CPU time: 0ns
CPU time (one time): 0ns
Gen 0: 0
Gen 1: 0
Gen 2: 0
采用委托
CodeTimer.Time( " Empty Method " , 10000000 , delegate () { } );
string a = "" ;
CodeTimer.Time( " String Concat " , 100000 , delegate () { a += " a " ; } );
StringBuilder s = new StringBuilder();
CodeTimer.Time( " StringBuilder Conca " , 100000 , delegate () { s.Append( " a " ); } );
测试结果
Thread Sleep
Time Elapsed: 2,989ms
Time Elapsed (one time):2,989ms
CPU time: 0ns
CPU time (one time): 0ns
Gen 0: 0
Gen 1: 0
Gen 2: 0
Empty Method
Time Elapsed: 156ms
Time Elapsed (one time):0ms
CPU time: 156,250,000ns
CPU time (one time): 15ns
Gen 0: 0
Gen 1: 0
Gen 2: 0
String Concat
Time Elapsed: 10,425ms
Time Elapsed (one time):0ms
CPU time: 10,406,250,000ns
CPU time (one time): 104,062ns
Gen 0: 4102
Gen 1: 2661
Gen 2: 2545
StringBuilder Conca
Time Elapsed: 4ms
Time Elapsed (one time):0ms
CPU time: 0ns
CPU time (one time): 0ns
Gen 0: 0
Gen 1: 0
Gen 2: 0