如何在Win32中获取CPU周期计数?

7
在Win32中,有没有一种方法可以获取一个唯一的CPU周期计数或类似的东西,这对于多个进程/语言/系统等是统一的?
我正在创建一些日志文件,但必须生成多个日志文件,因为我们托管了.NET运行时,并且我想避免从一个日志文件调用到另一个日志文件进行记录。因此,我想我只需生成两个文件,将它们合并,然后对它们进行排序,以获得涉及跨世界调用的连贯时间线。
然而,GetTickCount不会为每次调用增加,因此不可靠。是否有更好的数字,以便在排序时正确获取调用顺序?

编辑:感谢@Greg指引我使用QueryPerformanceCounter,解决了问题。

5个回答

12

这是一篇有趣的文章!建议不要使用RDTSC,而是使用QueryPerformanceCounter

结论:

使用普通的timeGetTime()进行计时在许多基于Windows的操作系统上并不可靠,因为系统定时器的粒度可能高达10-15毫秒,这意味着timeGetTime()只有10-15毫秒的准确度。[请注意,高粒度发生在像Windows NT、2000和XP这样的基于NT的操作系统上。Windows 95和98往往具有更好的粒度,大约为1-5毫秒]。
然而,如果您在程序开始时调用timeBeginPeriod(1)(并在结束时调用timeEndPeriod(1)),timeGetTime()通常会变得准确到1-2毫秒,并为您提供极其精确的计时信息。 Sleep()的行为类似;Sleep()实际休眠的时间长度与timeGetTime()的粒度相伴随,因此在调用了timeBeginPeriod(1)一次之后,Sleep(1)实际上会休眠1-2毫秒,Sleep(2)会休眠2-3毫秒,以此类推(而不是以高达10-15毫秒的增量休眠)。
对于更高精度的计时(亚毫秒级别的准确度),您可能希望避免使用汇编助记符RDTSC,因为它很难校准;相反,使用QueryPerformanceFrequencyQueryPerformanceCounter,它们的精度小于10微秒(0.00001秒)。
对于简单的计时,timeGetTime和QueryPerformanceCounter都很好用,而QueryPerformanceCounter显然更精确。但是,如果您需要进行任何类型的"定时暂停"(例如那些必要的帧速率限制),则需要小心不要坐在一个循环中调用QueryPerformanceCounter,等待它达到某个值;这将占用您100%的处理器。相反,考虑一种混合方案,在需要传递超过1毫秒的时间时调用Sleep(1)(不要忘记先调用timeBeginPeriod(1)!),然后只进入QueryPerformanceCounter 100%-busy循环,以完成您需要的最后不到1/1000秒的延迟。这将为您提供极其精确的延迟(精度高达10微秒),并且CPU使用率非常低。请参见上面的代码。

3
请避免使用timeBeginPeriod()函数,它会影响整个系统的调度器并可能导致节能问题。 - MSalters

9
您可以使用CPU指令RDTSC(假设您使用的是x86架构)。该指令提供了CPU周期计数器,但请注意它会非常快地增加到最大值,然后重置为0。正如维基百科文章所提到的,您最好使用QueryPerformanceCounter函数。

2

System.Diagnostics.Stopwatch.GetTimestamp() 返回自某个时间点(可能是计算机启动时)以来的CPU周期数,我从未见过在两次调用之间未增加。

CPU周期对于每台计算机都是特定的,因此您无法将其用于合并来自2台计算机的日志文件。


谢谢,我只需要合并在相同时间段内由同一台计算机生成的文件,这样就可以了。现在我只需要找出那个方法实际上叫什么:) - Lasse V. Karlsen

2

RDTSC输出可能取决于当前核心的时钟频率,对于现代CPU而言,其时钟频率既不是恒定的,也不在多核机器中保持一致。

使用系统时间,如果处理来自多个系统的源,请使用NTP时间源。您可以通过这种方式获得可靠,一致的时间读数;如果开销过大,使用HPET计算自上次已知可靠时间读数以来经过的时间比仅使用HPET更好。


1
使用GetTickCount并在合并日志文件时添加另一个计数器。这不会给出不同日志文件之间的完美顺序,但至少可以保持每个文件中所有日志的正确顺序。

3
滴答计数似乎与毫秒输出同时增加,这就是为什么我需要更精确的东西。 - Lasse V. Karlsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接