18

有时我会遇到使用指令读取 TSC 的代码,但在此之前rdtsc调用。cpuid

为什么需要打电话cpuid?我意识到这可能与具有 TSC 值的不同内核有关,但是当您按顺序调用这两条指令时究竟会发生什么?

4

3 回答 3

18

这是为了防止乱序执行。来自一个现在已经从网络上消失的链接(但在消失之前偶然被复制到这里),这段文字来自 John Eckerdal 的一篇题为“性能监控”的文章:

Pentium Pro 和 Pentium II 处理器支持乱序执行指令可以在您对它们进行编程时以另一个顺序执行。如果不加以注意,这可能是错误的根源。

为了防止这种情况,程序员必须序列化指令队列。这可以通过在 RDTSC 指令之前插入像 CPUID 指令这样的序列化指令来完成。

于 2010-05-27T02:32:39.867 回答
6

两个原因:

  • 正如 paxdiablo 所说,当 CPU 看到 CPUID 操作码时,它会确保执行所有先前的指令,然后获取 CPUID,然后再执行任何后续指令。如果没有这样的指令,CPU 执行管道可能最终会在您想要计时的指令之前执行 TSC。
  • 很大一部分机器无法跨内核同步 TSC 寄存器。如果你想从马的嘴里读到它-在http://msdn.microsoft.com/en-us/library/ee417693%28VS.85%29.aspx上敲自己. 因此,在测量 TSC 读数之间的间隔时,除非它们是在同一个内核上获取的,否则您将引入一个有效随机但可能恒定(见下文)的间隔 - 即使在启动后不久,它也可能很容易是几秒钟(是几秒钟) . 这有效地反映了 BIOS 在启动其他内核之前在单个内核上运行了多长时间,此外 - 如果您有任何令人讨厌的节能选项 - 增加了内核以不同频率运行或再次关闭导致的漂移。因此,如果您还没有将读取 TSC 寄存器的线程固定到同一个内核,那么您需要构建某种跨内核增量表并按顺序了解每个 TSC 样本的内核 ID(由 CPUID 返回)来补偿这个偏移量。这是您可以在 RDTSC 旁边看到 CPUID 的另一个原因,这确实是许多操作系统使用较新的 RDTSCP 将核心 ID 号存储到返回的额外 TSC_AUX[31:0] 数据中的原因。(可从 Core i7 和 Athlon 64 X2 获得,RDTSCP 在所有方面都是一个更好的选择 - 操作系统通常会为您提供前面提到的核心 id,对于 TSC 读取是原子的,防止指令重新排序)。
于 2012-06-08T06:53:33.130 回答
3

CPUID 正在序列化,防止 RDTSC 乱序执行。

这些天来,您可以安全地使用 LFENCE 代替。它被记录为在 Intel CPU 上的指令流上序列化(但不存储到内存中),现在也在 AMD 上为 Spectre 更新微码后。

https://hadibrais.wordpress.com/2018/05/14/the-significance-of-the-x86-lfence-instruction/解释了有关 LFENCE 的更多信息。

另请参阅https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/ia-32-ia-64-benchmark-code-execution-paper.pdf的方法使用 RDTSC P将 CPUID(或 LFENCE)保持在定时区域之外:

LFENCE     ; (or CPUID) Don't start the timed region until everything above has executed
RDTSC           ; EDX:EAX = timestamp
mov  ebx, eax   ; low 32 bits of start time

   code under test

RDTSCP     ; built-in one way barrier stops it from running early
LFENCE     ; (or CPUID) still use a barrier after to prevent anything weird
sub  eax, ebx   ; low 32 bits of end-start

另请参阅获取 CPU 周期数?有关 RDTSC 警告的更多信息,例如 constant_tsc 和 nonstop_tsc。

作为奖励,RDTSCP 会为您提供一个核心 ID。如果您想检查核心迁移,您也可以使用 RDTSCP 作为开始时间。但是,如果您的 CPU 具有这些constant_tsc功能,则包中的所有内核都应同步其 TSC,因此您通常在现代 x86 上不需要此功能。

正如@Tony 的回答所指出的,您可以从 CPUID 获取核心 ID。

于 2019-01-16T09:49:48.910 回答