zoukankan      html  css  js  c++  java
  • C#中精确计时的一点收获 Stopwatch

    http://www.cnblogs.com/jintianhu/archive/2010/09/01/1815031.html

     参考: https://www.cnblogs.com/kissdodog/archive/2013/05/20/3088874.html

    以下所有代码运行环境:Windows 2003, Intel(R) Core(TM) 2 Duo CPU E8400 @  3.00GHz 2.99GHz,2.96GB内存

    根据综合网上的一些文章,精确计时主要有以下几种方式

    1 调用WIN API中的GetTickCount

    [DllImport("kernel32")]
    static extern uint GetTickCount();

    从操作系统启动到现在所经过的毫秒数,精度为1毫秒,经简单测试发现其实误差在大约在15ms左右

    缺点:返回值是uint,最大值是2的32次方,因此如果服务器连续开机大约49天以后,该方法取得的返回值会归零

    用法:

    1
    2
    3
    uint s1 = GetTickCount();
    Thread.Sleep(2719);
    Console.WriteLine(GetTickCount() - s1);  //单位毫秒

    2 调用WIN API中的timeGetTime  推荐

    [DllImport("winmm")]
    static extern uint timeGetTime();

    常用于多媒体定时器中,与GetTickCount类似,也是返回操作系统启动到现在所经过的毫秒数,精度为1毫秒。

    一般默认的精度不止1毫秒(不同操作系统有所不同),需要调用timeBeginPeriod与timeEndPeriod来设置精度

    [DllImport("winmm")]
    static extern void timeBeginPeriod(int t);
    [DllImport("winmm")]
    static extern void timeEndPeriod(int t);

     缺点:与GetTickCount一样,受返回值的最大位数限制。

    用法:

    1
    2
    3
    4
    5
    timeBeginPeriod(1);
    uint start = timeGetTime();
    Thread.Sleep(2719);
    Console.WriteLine(timeGetTime() - start);  //单位毫秒
    timeEndPeriod(1);

     3 调用.net自带的方法System.Environment.TickCount

    获取系统启动后经过的毫秒数。经反编译猜测它可能也是调用的GetTickCount,但是它的返回值是int,而GetTickCount与timeGetTime方法的原型中返回值是DWORD,对应C#中的uint,难道.NET对System.Environment.TickCount另外还做了什么处理么?
    缺点:与GetTickCount一样,受返回值的最大位数限制。

    用法:

    1
    2
    3
    int aa = System.Environment.TickCount;
    Thread.Sleep(2719);
    Console.WriteLine(System.Environment.TickCount - aa); //单位毫秒

     :经过测试,发现GetTickCount、System.Environment.TickCount也可以用timeBeginPeriod与timeEndPeriod来设置精度,最高可将精度提高到1毫秒。不知是什么原因?

     4 调用WIN API中的QueryPerformanceCounter

    [DllImport("kernel32.dll ")]
    static extern bool QueryPerformanceCounter(ref long lpPerformanceCount);

    用于得到高精度计时器(如果存在这样的计时器)的值。微软对这个API解释就是每秒钟某个计数器增长的数值。
    如果安装的硬件不支持高精度计时器,函数将返回false需要配合另一个API函数QueryPerformanceFrequency。

    [DllImport("kernel32")]
    static extern bool QueryPerformanceFrequency(ref long PerformanceFrequency);

     QueryPerformanceFrequency返回硬件支持的高精度计数器的频率,如果安装的硬件不支持高精度计时器,函数将返回false。

    用法:

    1
    2
    3
    4
    5
    6
    7
    long a = 0;
    QueryPerformanceFrequency(ref a);
    long b = 0, c = 0;
    QueryPerformanceCounter(ref b);
    Thread.Sleep(2719);
    QueryPerformanceCounter(ref c);
    Console.WriteLine((c - b) / (decimal)a);  //单位秒

    精度为百万分之一秒。而且由于是long型,所以不存在上面几个API位数不够的问题。

    缺点:在一篇文章看到,该API在节能模式的时候结果偏慢,超频模式的时候又偏快,而且用电池和接电源的时候效果还不一样(笔记本)
    原文地址:http://delphi.xcjc.net/viewthread.php?tid=1570
    未经过超频等测试,如果是真的,那该API出来的结果就可能不准。

     5 使用.net的System.Diagnostics.Stopwatch类    推荐

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

    实际上它里面就是将QueryPerformanceCounter、QueryPerformanceFrequency两个WIN API封装了一下,如果硬件支持高精度,就调用QueryPerformanceCounter,如果不支持就用DateTime.Ticks来计算。

    用法:

    1
    2
    3
    4
    5
    Stopwatch sw = new Stopwatch();
    sw.Start();
    Thread.Sleep(2719);
    sw.Stop();
    Console.WriteLine(sw.ElapsedTicks / (decimal)Stopwatch.Frequency);
     
    //获取刻度值  ,计算毫秒数
    long start=Stopwatch.GetTampstamp()
    double dbl=(Stopwatch.GetTampstamp()-start)*1000/Stopwatch.frequency;
     
     

    6 使用CPU时间戳进行更高精度计时

    原文地址:http://www.chinaunix.net/jh/23/110190.html

    该方法的原理我不是很明白,硬件知识太匮乏了。精度是ns

    在C#中要用该方法必须先建立一个托管C++项目(因为要内嵌汇编),编译成DLL供c#调用,有点麻烦。

    C++代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // MLTimerDot.h
     
    #pragma once
     
    using namespace System;
     
    namespace MLTimerDot {
     
            //得到计算机启动到现在的时钟周期
            unsigned __int64 GetCycleCount(void)
            {
                    _asm  _emit 0x0F
                    _asm  _emit 0x31
            }
     
     
            //声明 .NET 类
            public __gc class MLTimer
            {
            public:
                    MLTimer(void)
                    {
                           
                    }
     
                    //计算时钟周期
                    UInt64 GetCount(void)
                    {
                            return GetCycleCount();
                    }
     
            };
    }

    C#调用:

    1
    2
    3
    4
    5
    6
    7
    long a = 0;
    QueryPerformanceFrequency(ref a);
     
    MLTimerDot.MLTimer timer = new MLTimerDot.MLTimer();
    ulong ss= timer.GetCount();
    Thread.Sleep(2719);
    Console.WriteLine((timer.GetCount() - ss) / (decimal)a);

    缺点:和QueryPerformanceCounter一样,结果不太稳定。

    我的结论:常规应用下timeGetTime完全够用了,将精度调到1毫秒,大部分境况都够用。System.Diagnostics.Stopwatch由于调用方便,也推荐使用。

     
     
    标签: c#
  • 相关阅读:
    mysq 日期相减
    说说时间观与时间管理——北漂18年(71)
    ionic之切换开关
    ionic之单选框
    SELECT ... LOCK IN SHARE MODE和SELECT ... FOR UPDATE locks在RR模式下可以看到最新的记录
    14.5.2.3 Consistent Nonlocking Reads 一致性非锁定读
    14.5.2.2 autocommit, Commit, and Rollback
    14.5.2 事务隔离级别
    对于唯一索引使用唯一条件搜索, InnoDB 只锁定找到的index record,不是它之前的区间
    mysql explain 解释
  • 原文地址:https://www.cnblogs.com/kelelipeng/p/10105808.html
Copyright © 2011-2022 走看看