获取高精度时间注意事项 (QueryPerformanceCounter , QueryPerformanceFrequency)

时间:2023-03-09 03:05:12
获取高精度时间注意事项 (QueryPerformanceCounter , QueryPerformanceFrequency)

花了很长时间才得到的经验,与大家分享。

1. RDTSC - 粒度: 纳秒级 不推荐
优势: 几乎是能够获得最细粒度的计数器
抛弃理由:

A) 定义模糊
- 曾经据说是处理器的cycle counter,但是后来似乎又不是了。
有的机器上每秒的TSC增长值等于CPU频率,有的却是一个不对应任何配置的数。到底是什么,Intel也没解释清楚。

B) 不准确
- 这是最重大的缺陷。再细的粒度,不准的话也没用,至少不能当时间用。
在有的CPU上,特别是支持变频技术的笔记本CPU上,TSC增长值会随着CPU的频率改变。忙的时候跑得快,闲得时候跑得慢。

2. QueryPerformanceCounter - 粒度: 1~100微秒级 不推荐
优势: 尽管比RDTSC粒度稍低,但是不存在RDTSC在变频CPU上的问题。
知道这个API的人估计都倾向于用这个,因为M$对这个API给出了比较明确的定义,就是每秒钟某个计数器增长的数值。
抛弃理由: 还是不准确

尽管没有源代码,但是从M$的帮助文档和知识库可以了解到,PerformanceCounter是依赖于主板上与PCI设备有关联的硬件。这就意味着,PerformanceCounter的结果还是会受到硬件频率,特别是总线频率的影响。

事实上,我在EeePC上测试的时候就发现,系统采用节能模式的时候PerformanceCounter出来的结果老是偏慢很多,超频模式的时候又偏快,而且用电池和接电源的时候效果还不一样!

3. timeGetTime - 粒度: 毫秒级 推荐
尽管粒度进一步降低,但是其无与伦比的优势就是,准确。
在任何机器上返回的都是当前系统的启动时间,精确到1毫秒。

使用注意事项:

A) 在NT系统上(据说)默认精度为10ms,但是可以用timeBeginPeriod来降低到1ms
B) 返回的是一个32位整数,所以要注意大约每49.71天会出现归零(不像前两个是64位数,要几百年才会归零)。
---------------------------------------------- 
-

实际上timeGetTime以及GetTickCount也是由QueryPerformanceCounter/QueryPerformanceFrequency得到的,因为做了除以频率的操作,所以得到的数值比较客观,准确

明显不是的。

我的代码就是用QueryPerformanceCounter / QueryPerformanceFrequency得到实际经过的时间。

测试方法为: 调用时间API, Sleep 500ms, 再调用时间API, 不停的循环。

用 两次timeGetTime的差值得到经过的毫秒数 (简称 Tick)
与 两次QueryPerformanceCounter的差值/QueryPerformanceFrequency得到经过的毫秒数 (简称 Counter)

比较发现:

在EeePC 正常模式下: Tick: 500ms, Counter: 500ms
在超频模式下: Tick: 500ms, Counter: 535ms
在节能模式下: Tick: 500ms, Counter: 260ms

可见用多媒体时钟timeGetTime始终是稳定的,但是PerformanceCounter就不行了。
---------------------------------------------- 
-

M$的有一个KB上提到芯片组Bug导致QueryPerformanceCounter有时候会往前跳几秒。描述原因的时候说是因为芯片组在PCI Bus上的Bug导致系统接受到奇怪的消息,系统为了保证稳定就会往PerformanceCounter上加几秒钟。从这个描述上就可以看出,PerformanceCounter是以PCI Bus的某个硬件作为基础的。

事实上,有的人说PerformanceCounter是系统接受的IRQ#0的计数器,但是还有一种说法是,在有的系统上PerformanceCounter会使用CPU的TSC...

总之,Performance Counter也不是一个可以100%靠得住的时间源。

要想你的代码在大量配置不同的机器上不出问题,目前只有依赖timerGetTime这个多媒体时钟。
---------------------------------------------- 
-

大概瞅了一下intel manual,vol3b (system programming guide B)里ch18第11和20节讲的是关于计时的
手册2b里RDTSC中除了说3b的18章外,还提到了3b的21章,这些你都仔细读过了之后得到上述关于RDTSC的结论的么?

er... 我的是经验,不是结论。靠读manual是不可能得到的,是我花了很长时间在很多机器上试验得到的结果。

有的机器上RDTSC的增幅是和频率一致的,但是多数是台式机,而且是老型号。
新的台式机和笔记本CPU的RDTSC的增幅明显不是频率,有说法是总线的主频。
Pentium M的TSC会随着speedstep变化;服务器的好像还挺稳定,但是我没有打开Linux内核的CPU Frequency Scaling,说不定打开了以后也会出现这个现象。

在多CPU下,每一个CPU的RDTSC是独立的;SMP下运行的线程可能会被放到不同的CPU下运行,这意味着两次读TSC可能会出现后面比前面小的情况,所以最好还要SetThreadAffinity,绑定在一块CPU上。

总之TSC用起来也很麻烦,而且太底层,很难在不同的配置下兼容。这个东西用来做单个机器的Profiling不错(本来就是为了这个目的),但是如果当成时间源的话就差了。
---------------------------------------------- 
-

我建议吧,和硬件有关的东西还是直接看官方的manual,贷是人家设计的,手册不可能还没你的经验靠谱
下面这段是vol 3b ch18.20中最开头的部分,建议你在自己已经做过之前,不要轻易下“靠读manual是不可能得到的”这种结论
The count of cycles, also known as clockticks, forms a the basis for measuring how 
long a program takes to execute. Clockticks are also used as part of efficiency ratios 
like cycles per instruction (CPI). Processor clocks may stop ticking under circum-
stances like the following:
? The processor is halted when there is nothing for the CPU to do. For example, the 
processor may halt to save power while the computer is servicing an I/O request. 
When Intel Hyper-Threading Technology is enabled, both logical processors must 
be halted for performance-monitoring counters to be powered down.
? The processor is asleep as a result of being halted or because of a power-
management scheme. There are different levels of sleep. In the some deep sleep 
levels, the time-stamp counter stops counting.
In addition, processor core clocks may undergo transitions at different ratios relative 
to the processor’s bus clock frequency. Some of the situations that can cause 
processor core clock to undergo frequency transitions include:
? TM2 transitions
? Enhanced Intel SpeedStep Technology transitions (P-state transitions)
----------------------------------------------