• C#中精确计时的一点收获 【转】


    C#中精确计时的一点收获 【转】

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

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

    1 调用WIN API中的GetTickCount

    [DllImport("kernel32")]
    staticexternuint
    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")]
    staticexternuint
    timeGetTime();

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

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

    [DllImport("winmm")]
    staticexternvoid timeBeginPeriod(int t);
    [DllImport(
    "winmm")]
    staticexternvoid 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 ")]
    staticexternbool QueryPerformanceCounter(reflong
    lpPerformanceCount);

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

    [DllImport("kernel32")]
    staticexternbool QueryPerformanceFrequency(reflong
    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);

    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由于调用方便,也推荐使用。

  • 相关阅读:
    X5webview完美去掉分享功能和缓存功能(2)
    bintray 在android3.2上传遇到的问题
    x5webview 自定义全屏界面
    X5webview去掉分享功能和缓存功能
    buglly热更新集成遇到的那些坑
    腾讯x5webview集成实战
    动态权限<三>华为小米特殊机制
    android 判断应用是否在前台显示
    动态权限<二>之淘宝、京东、网易新闻 权限申请交互设计对比分析
    android 图片二维码识别和保存(二)
  • 原文地址:https://www.cnblogs.com/xihong2014/p/13726165.html
Copyright © 2020-2023  润新知