具有可变时钟速度的多核系统中的QueryPerformance计数器

根据MSDN文章Game Timing and Multicore Processors, QueryPerformanceFrequency()和QueryPerformanceCounter()函数被认为是最好的。 但是在不支持的情况下,我可以使用timeGetTime()或GetTickCount()。

  1. QueryPerformanceFrequency()与CPU时钟是一样的,还是使用自己的时钟或者具有不随时间变化的自己的频率?
  2. 如果频率随机随机变化(特别是在笔记本电脑中)
  3. 我如何使用SetThreadAffinityMask函数? (我见过的一些代码使用函数将其改为“1”,然后使用计数器并将掩码再次更改为旧值,为什么?是正确的?)
  4. 只使用QueryPerformanceFrequency()函数一次并通过除以case / question 1中的频率计算delta时间值是否正确? 或者是由案例2修复?

  1. QPC的实施差异很大。 在某些情况下,它通常不是。
  2. 这将影响RDTSC,但不是QPC。
  3. 这是为了防止线程从一个CPU核心移动到另一个。 这可能有助于避免高分辨率计时方法报告消极时间传递(发生…)。 一般不建议。
  4. QPC的频率是恒定的。 至少在给定的系统上,至少在重新启动之前。

但是你不一定要问正确的问题

窗口上常用的四个定时函数是:GetTickCount,timeGetTime,QueryPerformanceCounter(QPC)和RDTSC

我的建议是:

游戏逻辑时间应该用timeGetTime来完成。 这是简单,可靠,并有足够的解决方案的目的。 (编辑:默认分辨率不同 – 你可以调用timeBeginPeriod来强制它达到1毫秒的分辨率)

GetTickCount不应该被使用。 对于游戏逻辑或性能监控来说,分辨率太差(64赫兹 – 一个令人讨厌的频率,因为它会以典型的显示器刷新率创建拍频)。 这是最快的定时功能调用IIRC,但我找不到一个弥补其分辨率差的情况。 (编辑:有传言说,timeBeginPeriod可以提高GetTickCount的分辨率 – 这个传言是FALSE)

RDTSC和QPC对于简单的游戏逻辑时序来说太不可靠/古怪,但更适合于性能测量。 如果你想要独立于CPU频率变化的单元,RDTSC有一些问题会让你很痛苦,你通常需要使用asm来使用它。 QPC通常是可以工作的…但是当错误出现的时候,它可能会出错,而且出错的方式很多(有的时候真的很慢,有的时候频繁出现小的负三角形,有的时候也不经常出现大的负三角形(而不是环绕),有时候完全是精神病等)。 RDTSC几乎总是更快,通常分辨率更好。 总的来说,我更喜欢RDTSC的内部使用,只是因为它更快,从而在测量时间内产生更少的失真。 在客户机器上,这是一个非常接近的要求 – QPC由于微软的推动而更容易被证明是合理的,并且它常常没有任何复杂的情况,但是在客户机器上可能遇到的各种各样的方式,房子是我认为的主要缺点。

QPF / QPC是最好的,如果你需要一个高精度的计时器 (返回值是纳秒,但这并不意味着精度是1纳秒)。 否则,只需使用GetTickCount() (以毫秒为单位)。 两个版本都应该正确处理可变的CPU频率(例如,在具有省电选项的笔记本电脑上)。

我不知道如何亲和面具可以帮助检索系统时间。

获得高精度时间的正确方法是调用QPF和QPC,并计算时间为:

 double seconds = QPC / QPF; 

编辑:

GetTickCount()的精度很差,比如5毫秒,但它仍然适用于大多数应用程序。 为了测量真正的小时间段,有一个选项:QPC / QPF。

我个人比较喜欢x86架构中的64位计数器的时间戳计数器,该计数器在每个内部时钟周期内递增。 它使用rdtsc指令读取,并返回edx:eax寄存器(x86-32)和rdx:rax(x86-64)中的计数器值。

教学方面有问题,但那是多年以前的事了。 今天的“绿色功能”导致负载相关的执行频率变化使得计算经过时间更加困难,但是经过的时钟周期不是问题。

 unsigned long long startCycle, endCycle, elapsedCycles, overhead; // @ start of program overhead=instruction_rdtsc (); overhead=instruction_rdtsc ()-overhead; // preparing to measure startCycle=instruction_rdtsc (); // (sequence to measure) endCycle=instruction_rdtsc (); elapsedCycles=endCycle-startCycle-overhead; 

指令本身的开销应该被确定。 我发现英特尔处理器的开销比AMD处理器要小。 开销应该多次测量 – 比如循环 – 找到最低的可能值。 测量的序列越长,开销就越小。 该指令可以在应用程序中插入永久的性能计量,以便能够在正常(非性能测试)执行下测量其实际性能。

由于流水线和无序执行问题,不应该测量很短的序列。 有人建议在rdtsc之前插入cpuid指令,但这只意味着实际的时钟数量大于它实际的数量。 我看到的周期数为30左右,而100左右或更高的周期总体上是可靠的。 中间有一个灰色地带。