【问题标题】:"cpuid" before "rdtsc"“rdtsc”之前的“cpuid”
【发布时间】:2011-02-24 11:26:24
【问题描述】:

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

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

【问题讨论】:

  • 除了 paxdiablo 的回答之外,请注意,即使是像 Pentium Pro、II 和 III 这样的单核也可以乱序执行。第6章来自Agner-Fog/microarchitecture

标签: assembly x86 rdtsc


【解决方案1】:

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

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

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

【讨论】:

  • 不应该真正从引用的文本中删除所有出处,现在看起来你在抄袭。
  • @Ross,文本来源的链接已经从网上消失了,所以在断链和不归属之间做出选择。如果你有什么建议,我愿意听。归因于不存在的东西似乎适得其反。希望编辑解决抄袭问题。
  • 这就是为什么链接不能真正算作归因。正确的归属应该包括作者的姓名(John Eckerdal)以及您从中复制文本的任何内容的标题或描述(显然称为“性能监控”,但也称为“gem0029”)。幸运的是,Wayback Machine 具有您最初链接到存档的页面,因此我能够找到此信息。 web.archive.org/web/20160602175959/http://dflund.se:80/~john_e/…
  • 很好,罗斯,我已经添加了对这篇文章的引用,尽管不是实际的 Wayback 链接。我犹豫是否要添加另一个链接,所以我将仅依靠文字描述。
【解决方案2】:

两个原因:

  • 正如 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 读取是原子的, 并且 防止指令重新排序)。

【讨论】:

    【解决方案3】:

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

    如今,您可以安全地改用 LFENCE。它被记录为在 Intel CPU 上的指令流上进行序列化(但不存储到内存中),现在在 AMD 的 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,了解使用 RDTSCP 将 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。

    【讨论】:

      猜你喜欢
      • 2021-11-20
      • 2013-07-19
      • 1970-01-01
      • 2020-05-02
      • 2011-09-19
      • 2013-01-09
      • 2010-12-12
      • 2012-01-23
      • 2012-10-24
      相关资源
      最近更新 更多