“rdtsc”之前的“cpuid”

时间:2010-05-27 02:29:12

标签: assembly x86 rdtsc

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

为什么要调用cpuid?我意识到它可能与具有TSC值的不同内核有关,但当你按顺序调用这两个指令时,完全会发生什么?

3 个答案:

答案 0 :(得分:18)

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

  

Pentium Pro和Pentium II处理器支持无序执行指令,可以在编程时以其他顺序执行。如果不加以处理,这可能是错误的来源。

     

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

答案 1 :(得分:6)

有两个原因:

  • 正如paxdiablo所说,当CPU看到CPUID操作码时,它确保执行所有先前的指令,然后在执行任何后续指令之前获取CPUID。如果没有这样的指令,CPU执行流水线可能会在您想要的时间之前执行TSC。
  • 很大一部分机器无法跨核心同步TSC寄存器。在你想要从 a 马的嘴里读出它 - 在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读取的原子,防止指令重新排序)。

答案 2 :(得分:1)

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

这几天,您可以放心使用LFENCE。它被记录为在Intel CPU上的指令流上进行序列化(但不存储到内存),而在针对Spectre的微代码更新后,现在也在AMD上进行序列化。

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

另请参阅Get CPU cycle count?,以获取有关RDTSC注意事项的更多信息,例如constant_tsc和nonstop_tsc。

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

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