关于高精度计时器
一、毫秒级精度
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]QueryPerformanceCounter、QueryPerformanceFrequency
[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的缓存功能,而这一部分是不可见的。

浙公网安备 33010602011771号