从 Delphi 6 开始,您可以使用 x86 Timestamp 计数器。
这会计算 CPU 周期,在 1 Ghz 处理器上,每个计数需要一纳秒。
没有比这更准确的了。
function RDTSC: Int64; assembler;
asm
// RDTSC can be executed out of order, so the pipeline needs to be flushed
// to prevent RDTSC from executing before your code is finished.
// Flush the pipeline
XOR eax, eax
PUSH EBX
CPUID
POP EBX
RDTSC //Get the CPU's time stamp counter.
end;
在 x64 上,以下代码更准确,因为它不受CPUID
.
rdtscp // On x64 we can use the serializing version of RDTSC
push rbx // Serialize the code after, to avoid OoO sneaking in
push rax // subsequent instructions prior to executing RDTSCP.
push rdx // See: http://www.intel.de/content/dam/www/public/us/en/documents/white-papers/ia-32-ia-64-benchmark-code-execution-paper.pdf
xor eax,eax
cpuid
pop rdx
pop rax
pop rbx
shl rdx,32
or rax,rdx
使用上面的代码获取执行代码之前和之后的时间戳。
最准确的方法可能和容易的馅饼。
请注意,您需要至少运行 10 次测试才能获得良好的结果,第一次通过时缓存会变冷,随机硬盘读取和中断可能会影响您的计时。
因为这个东西非常准确,如果你只计时第一次运行,它可能会给你错误的想法。
如果 CPU 变慢,为什么不应该使用 QueryPerformanceCounter()
QueryPerformanceCounter()
会给出相同的时间,它可以补偿 CPU 节流。如果您的 CPU 由于过热或其他原因而减速,RDTSC 将为您提供相同数量的周期。
因此,如果您的 CPU 开始过热并需要减速,QueryPerformanceCounter()
则会说您的例程需要更多时间(这是误导性的),而 RDTSC 会说它需要相同数量的周期(这是准确的)。
这是您想要的,因为您对代码使用的 CPU 周期数量感兴趣,而不是挂钟时间。
来自最新的英特尔文档:http ://software.intel.com/en-us/articles/measure-code-sections-using-the-enhanced-timer/?wapkw=%28rdtsc%29
使用处理器时钟
这个计时器非常准确。在具有 3GHz 处理器的系统上,此计时器可以测量持续时间少于 1 纳秒的事件。[...] 如果在目标代码运行时频率发生变化,则最终读数将是多余的,因为初始读数和最终读数不是使用相同的时钟频率获取的。在此期间发生的时钟滴答数将是准确的,但经过的时间将是未知的。
何时不使用 RDTSC
RDTSC 对于基本时序很有用。如果您在单 CPU 机器上为多线程代码计时,RDTSC 可以正常工作。如果您有多个 CPU,则 startcount 可能来自一个 CPU,而 endcount 可能来自另一个。
所以不要使用 RDTSC 在多 CPU 机器上对多线程代码进行计时。在单 CPU 机器上它可以正常工作,或者在多 CPU 机器上的单线程代码也很好。
还要记住,RDTSC 计算 CPU 周期。如果有一些需要时间但不使用 CPU 的东西,比如磁盘 IO 或网络,那么 RDTSC 就不是一个好工具。
但是文档说 RDTSC 在现代 CPU 上并不准确
RDTSC不是跟踪时间的工具,而是跟踪 CPU 周期的工具。
为此,它是唯一准确的工具。跟踪时间的例程在现代 CPU 上并不准确,因为 CPU 时钟不像以前那样是绝对的。