ExecutionStopwatch
一个测量线程执行所花费 CPU 时间的 Stopwatch 实现
介绍
一个常见的诊断任务是测量代码的性能。为此,.NET Framework 提供了 System.Diagnostics.Stopwatch 类。然而,这并非真正用于代码性能测量,并且会返回不准确的值(在 ExecutionStopwatch 与 .NET Stopwatch 段落中解释)。
ExecutionStopwatch 提供了一种准确测量代码性能的方法。
ExecutionStopwatch 与 .NET Stopwatch
.NET Framework 已经包含一个时间测量机制,该机制位于 Stopwatch 类中(位于 System.Diagnostics 下)。但是,该类的唯一目的是测量经过的“实际”时间。这意味着,如果您尝试使用 Stopwatch 类来测量方法的执行时间,您也会测量其他后台线程花费的时间。从理论上讲,您希望每次操作系统切换到另一个线程时调用“Stop()”,因为您不希望测量 CPU 花费在执行无关工作上的时间。
ExecutionStopwatch 正是实现了这一点——CPU 花费在执行您的当前线程上的时间。执行其他系统线程花费的时间将不会被计算在内。
这是通过使用 Win32 函数 GetThreadTimes 来完成的,该函数返回与特定线程相关的时间值,而不是系统的全局时间(如 Stopwatch 类所做的那样)。
演示
为了演示 .NET 的原始 Stopwatch 类与 ExecutionStopwatch 类之间的行为差异,我提出了以下示例
class Program
{
	static void Main()
	{
		Stopwatch regularSW = new Stopwatch();
		ExecutionStopwatch executionSW = new ExecutionStopwatch();
		regularSW.Start();
		executionSW.Start();
		// the thread won't be executed for 5 seconds
		Thread.Sleep(5000);
		regularSW.Stop();
		executionSW.Stop();
		Console.WriteLine("Regular Stopwatch: {0}", regularSW.Elapsed);
		Console.WriteLine("Execution Stopwatch: {0}", executionSW.Elapsed);
		// Output:
		// 	Regular Stopwatch: 00:00:04.9900562
		// 	Execution Stopwatch: 00:00:00
	}
}
差异可以立即看出。虽然 .NET Stopwatch 测量了 4.9 秒,但 ExecutionStopwatch 测量了约 0 秒(Windows 的时间精度约为 15 毫秒)。
虽然 Stopwatch 测量了经过的总时钟时间,但 ExecutionStopwatch 测量了 CPU 花费在执行代码上的时间。 
源代码
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace DiagnosticsUtils
{
	public class ExecutionStopwatch
	{
		[DllImport("kernel32.dll")]
		private static extern long GetThreadTimes
			(IntPtr threadHandle, out long createionTime,
			 out long exitTime, out long kernelTime, out long userTime);
		[DllImport("kernel32.dll")]
		private static extern IntPtr GetCurrentThread();
		private long m_endTimeStamp;
		private long m_startTimeStamp;
		private bool m_isRunning;
		public void Start()
		{
			m_isRunning = true;
			long timestamp = GetThreadTimes();
			m_startTimeStamp = timestamp;
		}
		public void Stop()
		{
			m_isRunning = false;
			long timestamp = GetThreadTimes();
			m_endTimeStamp = timestamp;
		}
		public void Reset()
		{
			m_startTimeStamp = 0;
			m_endTimeStamp = 0;
		}
		public TimeSpan Elapsed
		{
			get
			{
				long elapsed = m_endTimeStamp - m_startTimeStamp;
				TimeSpan result = 
					TimeSpan.FromMilliseconds(elapsed / 10000);
				return result;
			}
		}
		public bool IsRunning
		{
			get { return m_isRunning; }
		}
		private long GetThreadTimes()
		{
			IntPtr threadHandle = GetCurrentThread();
			long notIntersting;
			long kernelTime, userTime;
			long retcode = GetThreadTimes
				(threadHandle, out notIntersting,
				out notIntersting, out kernelTime, out userTime);
			bool success = Convert.ToBoolean(retcode);
			if (!success)
				throw new Exception(string.Format
				("failed to get timestamp. error code: {0}", 
				retcode));
			long result = kernelTime + userTime;
			return result;
		}
	}
}
历史
- 2008年11月22日:1.0 - 原始文章




