.NET v2.0 下的高精度计数器 —— Stopwatch [.NET v2.0, C#]

.NET v2.0 下的高精度计数器 —— Stopwatch [.NET v2.0, C#]

 

Written by Allen Lee

 

无论你是搞技术研究还是搞项目开发,高精度的时间测量在很多场合下都是必需的。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 的内部。

posted @ 2005-08-16 22:23 Allen Lee 阅读(4003) 评论(10)  编辑 收藏 网摘 所属分类: .NET

  回复  引用  查看    
#1楼 2005-08-16 23:23 | 萧寒      
当 Stopwatch 检测到当前的系统和硬件不支持高精度的计数器时,它将转用我们所熟悉的 DateTime 做法。

???????
这句没明白,什么样系统和硬件会不支持高精度计数器?
  回复  引用  查看    
#2楼 [楼主]2005-08-17 00:07 | Allen Lee      
To 萧寒:

这句话只是描述了 Stopwatch 内部的处理方式,说实话,我也没有见过现在有什么硬件是支持 .NET 却不支持高精度计数器。至于 Stopwatch 的这种做法是保险还是是多余,就见仁见智了。值得注意的是,Stopwatch 不支持 .NET CF。
  回复  引用  查看    
#3楼 2005-08-17 04:28 | xiaotie      
我刚才测试了一下。Stopwatch和QueryPerfCounter精度是一样的(测试ElapsedTicks)。Stopwatch和QueryPerfCounter我测试结果每次stop()和start()调用耗时是4个ticks,最小时间单位1个tick,对我笔记本来说是270ns。
  回复  引用  查看    
#4楼 2005-08-17 08:17 | yinh      
我到是把stopwatch的代码reflector出来了,不过有个类不可用,说是什么private什么的。最后也就没有继续下去了。
  回复  引用  查看    
#5楼 [楼主]2005-08-17 09:25 | Allen Lee      

To yinh:

你所指的那个类应该就是 SafeNativeMethods,它被声明为 internal,对于 Stopwatch 来说,该类的有效成分是:(该类其他成分略去)

internal static class SafeNativeMethods 
{
    [DllImport(
"kernel32.dll")]
    
public static extern bool QueryPerformanceCounter(out long value);
    [DllImport(
"kernel32.dll")]
    
public static extern bool QueryPerformanceFrequency(out long value);
}


  回复  引用  查看    
#6楼 2005-08-17 11:10 | xiaotie      
最后测试了一下用汇编。
(1)
extern "C"
{
__declspec(dllexport) unsigned __int64 GetCycleCount(void)
{
_asm _emit 0x0F;
_asm _emit 0x31;
}
}
(2)
public sealed class Enviroment
{
[DllImport("TimeStamp.dll")]
public static extern long GetCycleCount();

[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceFrequency(out long lpFrequency);

[DllImport("KERNEL32")]
private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
}
(3)
static void Main(string[] args)
{
long start,end;
start=Enviroment.GetCycleCount();
end=Enviroment.GetCycleCount();

for(int i=0;i<1000;i++)
{
start=Enviroment.GetCycleCount();
end=Enviroment.GetCycleCount();
Console.WriteLine(end-start);
}
}

这个东西行为很古怪,随着i增加,end-start慢慢的从3000多收敛到247.也就是说测量绝对的时间,误差在200ns上下,测定相对时间,通过校准可以下降到10ns以下。一次调用要花这个多时钟周期呀!不知道还又没办法进一步提高。
  回复  引用    
#7楼 2005-08-17 11:30 | ewate [未注册用户]
大哥啊,下次能把原文地址帖上吗??
真是好东西啊
  回复  引用    
#8楼 2005-08-29 13:26 | n43e120 [未注册用户]
遥测导弹飞人马,如果,你真有个雷达,
2008 可能用得上这套
我就的
System.Environment.TickCount
Do
Dim i as Uinteger
Loop Until i > xxxx
System.Environment.TickCount
循环 10^n 整数不久行了
  回复  引用    
#9楼 2006-06-06 21:44 | 安羽 [未注册用户]
在 Windows 中跟踪时间有几种不同的方法:

1.
System.Windows.Forms.Timer:这是 Windows 编程中最常用的计时器。虽然它很容易使用,但它只有 1/18 秒的分辨率。由于我们每秒最多可以有一千帧,因此这样的分辨率对于游戏程序而言不是太令人满意。

2.
timeGetTime:这种 Windows DLL 在有些版本的 Windows 上提供 1 微秒的分辨率,在 Windows NT 上为 5 微秒。这种变化太大,而且我们确实不想通过检查游戏运行的操作系统来查看我们是否需要调整计时器的值。

3.
System.TickCount:这种托管调用返回滴答数(表示毫秒数)。这接近于我们的期望,但我们可以做得更好。

4.
QueryPerformanceCounter:这是首选使用的计时器,其分辨率小于 1 微秒。这是在游戏开发中最常用的计时器。


编写最后一种计时器需要一定的技巧,因为它要求调用 Windows 中的一个低级 DLL(kernel32,如果您需要知道)。

  回复  引用    
#10楼 2007-08-09 10:27 | Arcrest [未注册用户]
有了stopwatch根本就不需要去声明windows api的2个高精度计时器函数
Stopwatch timer = new Stopwatch();
timer.Start();
DoSomething();
timer.Stop();
Console.WriteLine("elapsed time:{0}ms", (timer.ElapsedTicks+0.0f)*1000/Stopwatch.Frequency);





标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2005-10-23 07:48 编辑过
Google站内搜索
[推荐职位]上海盛大网络招聘.Net开发工程师

China-pub 计算机图书网上专卖店!6.5万品种 2-8折!
近千种 9-95 新二手计算图书火热销售中!
开发者征途系统新作:《设计模式——基于C#的工程化实现及扩展》

相关文章:

相关链接: