65.9K
CodeProject 正在变化。 阅读更多。
Home

ExecutionStopwatch

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (18投票s)

2008年11月22日

CPOL

2分钟阅读

viewsIcon

59211

downloadIcon

587

一个测量线程执行所花费 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 - 原始文章
© . All rights reserved.