zoukankan      html  css  js  c++  java
  • 关于高精度计时器

    一、毫秒级精度

        1、[.NET] System.Environment.TickCount

           获取系统启动后经过的毫秒数,包装了GetTickCount

        2、[WINAPI] GetTickCount 

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

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

        3、[WINAPI] timeGetTime 

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

          常用于多媒体定时器中,与GetTickCount类似,也是返回操作系统启动到现在所经过的毫秒数,最高精度为1毫秒。一般默认的精度不止1毫秒(在NT系统上据说默认精度为10ms,但是可以用timeBeginPeriod来降低到1ms),需要调用timeBeginPeriod与timeEndPeriod来设置精度

    二、微秒级精度

        1、[.NET] System.Diagnostics.Stopwatch

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

        2、[WINAPI]QueryPerformanceCounterQueryPerformanceFrequency

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

          QueryPerformanceCounter用于得到高精度计时器(如果存在这样的计时器)的值。如果安装的硬件不支持高精度计时器,函数将返回false。

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

            但是据说该API在节能模式的时候结果偏慢,超频模式的时候又偏快,而且用电池和接电源的时候效果还不一样(笔记本)。没有测试过。

    二、纳秒级精度

        [ASM] rdtsc

        机器码:0x0F, 0x31

        C++中使用方法:

    inline __declspec(naked) unsigned long Tick()
    {
    	_asm _EMIT 0x0F;
    	_asm _EMIT 0x31;
    	_asm ret;
    }
    

        C#中使用方法(C#中内联汇编):

    static class Helper
    {
        [DllImport("kernel32.dll")]
        static extern int VirtualProtect(IntPtr lpAddress, int dwSize, int flNewProtect, ref int lpflOldProtect);
        delegate ulong GetTickDelegate();
        static readonly IntPtr Addr;
        static readonly GetTickDelegate getTick;
        static readonly byte[] asm = { 0x0F, 0x31, 0xC3 };//rdtsc, ret的机器码
        static Helper()
        {
            Addr = Marshal.AllocHGlobal(3);
            int old = 0;
            VirtualProtect(Addr, 3, 0x40, ref old);
            Marshal.Copy(asm, 0, Addr, 3);
            getTick = (GetTickDelegate)Marshal.GetDelegateForFunctionPointer(Addr, typeof(GetTickDelegate));
        }
    
        public static ulong Tick
        {
            get
            {
                return getTick();
            }
        }
    }
    

      调用:Helper.Tick

    以下是百度看到的:

    这个指令在超线程和多核CPU上用来计算时间不是很准确

        1 根据intel的介绍,由于在现代的处理器中都具有指令乱序执行的功能,因此在有些情况下rdtsc指令并不能很好的反映真实情况。解决方法是,在rdtsc之前加一些cpuid指令,使得rdtsc后面的指令顺序执行。

        2 另外,rdtsc是一条慢启动的指令,第一次执行需要比较长的启动时间,而第二次之后时间就比较短了,也就是说,这条指令在第一次工作时需要比较长的时钟周期,之后就会比较短了。所以可以多运行几次,避过第一次的消耗。

        3 大家在测试某一个函数的cpu周期的时候,如果精度要求很高,需要减去rdtsc的周期消耗。我在至强2.6G上测试的结果是大约500多个时钟周期,我想这是应该考虑在内的,很多小的函数也就是几K个时钟周期。

        4 一定要注意cache的影响。如果你在对同一组数据进行操作,第一次操作往往要比后面几次时间开销大,原因就在于cache的缓存功能,而这一部分是不可见的。

  • 相关阅读:
    线段树
    2016.9.4
    使用CSS代码修改博客模板
    爬虫
    PHP初学[DAY2]
    2016.8.23
    一个自动设置游戏房间的脚本
    可逆矩阵生成
    #2284. 接水果(fruit)
    #3762. 有趣的数(number)
  • 原文地址:https://www.cnblogs.com/wmesci/p/2736010.html
Copyright © 2011-2022 走看看