代码改变世界

.NET v2.0 下的高精度计数器--Stopwatch 定义和实例

2009-08-14 13:37  周国选  阅读(719)  评论(0编辑  收藏  举报
命名空间:System.Diagnostics

Stopwatch 实例可以测量一个时间间隔的运行时间,也可以测量多个时间间隔的总运行时间。在典型的 Stopwatch 方案中,先调用 Start 方法,然后调用 Stop 方法,最后使用 Elapsed 属性检查运行时间。

Stopwatch 实例或者在运行,或者已停止;使用 IsRunning 可以确定 Stopwatch 的当前状态。使用 Start 可以开始测量运行时间;使用 Stop 可以停止测量运行时间。通过属性 ElapsedElapsedMillisecondsElapsedTicks 查询运行时间值。当实例正在运行或已停止时,可以查询运行时间属性。运行时间属性在 Stopwatch 运行期间稳固递增;在该实例停止时保持不变。

默认情况下,Stopwatch 实例的运行时间值相当于所有测量的时间间隔的总和。每次调用 Start 时开始累计运行时间计数;每次调用 Stop 时结束当前时间间隔测量,并冻结累计运行时间值。使用 Reset 方法可以清除现有 Stopwatch 实例中的累计运行时间。

Stopwatch 在基础计时器机制中对计时器的刻度进行计数,从而测量运行时间。如果安装的硬件和操作系统支持高分辨率性能的计数器,则 Stopwatch 类将使用该计数器来测量运行时间;否则,Stopwatch 类将使用系统计数器来测量运行时间。使用 FrequencyIsHighResolution 字段可以确定实现 Stopwatch 计时的精度和分辨率。

Stopwatch 类为托管代码内与计时有关的性能计数器的操作提供帮助。具体说来,Frequency 字段和 GetTimestamp 方法可以用于替换非托管 Win32 API QueryPerformanceFrequencyQueryPerformanceCounter

Note注意

在多处理器计算机上,线程在哪个处理器上运行无关紧要。但是,由于 BIOS 或硬件抽象层 (HAL) 中的 bug,在不同的处理器上可能会得出不同的计时结果。若要为线程指定处理器关联,请使用 ProcessThread.ProcessorAffinity 方法。



无论你是搞技术研究还是搞项目开发,高精度的时间测量在很多场合下都是必需的。xiaotie 在其《dotnet下时间精度测量》提到了一个 QueryPerfCounter,它对目前还在使用 Microsoft .NET Framework v1.1 的开发人员来说的确是一个好帮手。但若你已经用上了 Microsoft .NET Framework v2.0,那么 Stopwatch 将会成为你进行高精度时间测量的不二选择。

 

1. 测试 Stopwatch

这里,我借用一下 xiaotie 在《dotnet下时间精度测量》的测试代码,并给出对应的运行结果:

// Code #01

static void Test1()
{
    Stopwatch sw = 
new Stopwatch();
    sw.Start();
    sw.Stop();
    Console.WriteLine("Stopwatch 时间精度:{0}ms", sw.ElapsedMilliseconds);
}


// Output:
//
// Stopwatch 时间精度:0ms


static void Test2()
{
    Stopwatch sw = 
new Stopwatch();

    
int loop = 10000;
    
int exCount = 0;

    
for (int i = 0; i < loop; i++)
    
{
        sw.Reset();
        sw.Start();
        sw.Stop();
        
if (sw.ElapsedMilliseconds != 0)
        
{
            exCount++;
            Console.WriteLine("Stopwatch 时间精读异常:{0}ms", sw.ElapsedMilliseconds);
        }

    }


    Console.WriteLine("共进行{0}次 Stopwatch 的时间精度测试", loop);
    Console.WriteLine("其中{0}次 Stopwatch 的时间精度异常", exCount);
    Console.WriteLine("时间校准有效性{0}%", 100 - (100.0 * exCount) / loop);
}


// Output:
//
// 共进行10000次 Stopwatch 的时间精度测试
// 其中0次 Stopwatch 的时间精度异常
// 时间校准有效性100%


static void Test3()
{
    Stopwatch sw = 
new Stopwatch();

    
for (int i = 0; i < 200; i++)
    
{
        sw.Reset();
        sw.Start();
        sw.Stop();
        Console.WriteLine("Stopwatch 时间精度:{0}ms", sw.ElapsedMilliseconds);

        sw.Reset();
        sw.Start();
        
int j = 0;
        
for (j = 0; j < i; j++)
        
{
            
int l;
        }

        sw.Stop();
        Console.WriteLine("第{0}次循环,耗时{1}ms", i, sw.ElapsedMilliseconds);

        Console.WriteLine();
    }

}


// Partial output:
//
// Stopwatch 时间精度:0ms
// 第10次循环,耗时0ms
//
// Stopwatch 时间精度:0ms
// 第12次循环,耗时0ms
//
// Stopwatch 时间精度:0ms
// 第16次循环,耗时0ms

从测试结果中可以看到,Stopwatch 绝对能够满足挑剔的你!怎么样?手痒了吗?

 

2. 应用 Stopwatch

Stopwatch 位于 System.Diagnostics 命名空间中,它的使用方法非常简单,只要你会用一般的计时器,你就会使用它。

2.1 创建实例

你可以通过 new 或者 Stopwatch.StartNew() 来创建一个 Stopwatch 实例:

// Code #02

Stopwatch sw1 = 
new Stopwatch();
Stopwatch sw2 = Stopwatch.StartNew();

使用 StartNew() 会创建一个 Stopwatch 实例并马上开始计时,即等效于如下代码:

// Code #03

Stopwatch sw2 = 
new Stopwatch();
sw2.Start();

2.2 测量时间

// Code #04

sw1.Start();
// Do something here
sw2.Stop();
long elaspsedMilliseconds = sw1.ElaspedMilliseconds;

2.3 应用示例

微软官方提供了一系列示范程序用于演示 Visual Studio 2005,其中 Basic Class Library 部分带有一个 Stopwatch 的 Windows Forms 示例程序,你可以到 101 Samples for Visual Studio 2005 下载示例代码。

 

3. 深入 Stopwatch

Stopwatch 内部也调用了 QueryPerformanceCounter() 和 QueryPerformanceFrequency() 两个函数,与 QueryPerfCounter 不同的是,当 Stopwatch 检测到当前的系统和硬件不支持高精度的计数器时,它将转用我们所熟悉的 DateTime 做法。

Stopwatch 在其静态构造器中调用 QueryPerformanceFrequency(),透过该函数的返回值判断当前的系统和硬件是否支持高精度的计数器,并设置 IsHighResolution 属性的值。Stopwatch 内部用于计算时间间隔的主要方法都会根据 IsHighResolution 的值来选择合适的计算方法。有兴趣的话,你可以使用 Reflector 探究一下 Stopwatch 的内部。