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 - 原始文章