1秒 = 1000毫秒 = 1000 000微秒 = 1000 000 000 纳秒
大家都知道windows下的时间计数器提供的是毫秒级的时间计数。那么怎么提供一个精度更高的计数器呢。在Intel Pentium以上级别的CPU中,有一个称为“时间戳(Time Stamp)”的部件,它以64位无符号整型数的格式,记录了自CPU上电以来所经过的时钟周期数。对于一个主频为1GHz的CPU,则其一个时钟周期就是一纳秒。由于目前的CPU主频都非常高,因此这个部件可以达到 纳秒级的计时精度。在Pentium以上的CPU中,提供了一条机器指令RDTSC(Read Time Stamp Counter)来读取这个时间戳的数字,并将其保存在EDX:EAX寄存器对中。由于EDX:EAX寄存器对恰好是Win32平台下C++语言保存函数 返回值的寄存器,所以我们可以把这条指令看成是一个普通的函数调用。像这样:
inline unsigned __int64 GetCPUCycleCount()
{
__asm RDTSC
}
如果RDTSC不被C++的内嵌汇编器直接支持,我们还可以要用_emit伪指令直接嵌入该指令的机器码形式0X0F、0X31,如下:
inline unsigned __int64 GetCPUCycleCount()
{
__asm _emit 0x0F
__asm _emit 0x31
}
以后在需要计数器的场合,可以像使用普通的Win32 API一样,调用两次GetCPUCycleCount函数,比较两个返回值的差,然后利用CPU的时钟周期的差数就可以计算出一个纳秒级的计时。公式如下
时间 = CPU时钟周期数差数 / CPU主频速率(Hz)
vc中的实现代码如下:
// 纳秒 微秒 时间精度的实现
#include <stdio.h>
#include <windows.h>
#include <mmsystem.h> // 使用timeGetTime() 需要包含此头文件
#pragma comment(lib,"winmm.lib") // 使用timeGetTime() 需要包含此lib
// 微秒级
class MicrosecondTimer
{
public:
inline MicrosecondTimer()
{
QueryPerformanceFrequency(&freq);
}
inline void Start()
{
QueryPerformanceCounter(&startCount);
}
// 返回微秒
inline UINT64 Stop()
{
QueryPerformanceCounter(&endCount);
UINT64 decrease = endCount.QuadPart - startCount.QuadPart;
double elapsed = decrease / (double)freq.QuadPart;
return UINT64(elapsed*1000000);
}
private:
LARGE_INTEGER freq;
LARGE_INTEGER startCount;
LARGE_INTEGER endCount;
};
// 纳秒级
// 连续调用start和stop所需的时间大概是62ns
class NanosecondTimer
{
public:
inline NanosecondTimer()
{
startT = 0;
frequency = CalCPUFrequency();
}
//得到计算机启动到现在的时钟周期
static inline UINT64 GetCPUCycleCount(void)
{
// __asm _emit 0x0F
// __asm _emit 0x31
__asm RDTSC
}
// 得到CPU的频率
static inline double CalCPUFrequency()
{
LARGE_INTEGER liFreq,liStart,liEnd;
QueryPerformanceFrequency(&liFreq);
QueryPerformanceCounter(&liStart);
UINT64 startCounts = GetCPUCycleCount();
Sleep(1000);
QueryPerformanceCounter(&liEnd);
UINT64 endCounts = GetCPUCycleCount() - startCounts;
double elapsed = (liEnd.QuadPart - liStart.QuadPart) / (double)(liFreq.QuadPart); // 微秒级精度
double freq = endCounts / (elapsed*1000000);
return freq;
}
inline void Start()
{
startT = GetCPUCycleCount();
}
inline UINT64 Stop()
{
UINT64 endT = GetCPUCycleCount() - startT;
return UINT64( endT * 1000 / frequency );
}
inline double Frequency()
{
return frequency;
}
private:
UINT64 startT;
double frequency; // MHz/s
};
int main(int argc, CHAR* argv[])
{
NanosecondTimer NT;
MicrosecondTimer MT;
DWORD t1 = 0, t2 = 0;
UINT64 ns = 0 ;
UINT64 ms = 0;
for (int i = 0; i < 10 ; i++ )
{
NT.Start();
MT.Start();
Sleep(1);
ms = MT.Stop();
ns = NT.Stop();
printf("CPU:%f; %f \n %I64d; %I64d; \r\n",NT.CalCPUFrequency(),cpuc.GetCpuFrequency() , ns, ms);
}
getchar();
return 0;
}
事实上在windows中每次调用代码指令的时间都不稳定,所以这是很难真正的精确到纳秒级的。windows下最多只能提供微秒级的精确度。