【问题标题】:How to ensure that RDTSC is accurate?如何保证RDTSC是准确的?
【发布时间】:2016-06-19 14:11:35
【问题描述】:

我了解到 RDTSC 可能会给出错误的读数,因此不应依赖它。
这是真的吗?如果是这样,我们可以做些什么?

【问题讨论】:

    标签: x86 x86-64 cpuid rdtsc


    【解决方案1】:

    非常旧的 CPU 有一个准确的 RDTSC。

    问题

    但是较新的 CPU 有问题。
    工程师们认为 RDTSC 非常适合显示时间。
    但是,如果 CPU 限制频率,RDTSC 就无法告诉时间。
    上述脑残工程师随后决定通过让 TSC 始终以相同的频率运行来“解决”这个问题,即使 CPU 变慢也是如此。

    这具有 TSC 可用于显示经过(挂钟)时间的“优势”。然而,它使 TSC 无用 对分析的用处不大。

    如何判断你的 CPU 有没有坏

    您可以通过读取 CPUID 中的TSC_invariant 位来判断您的 CPU 是否正常。

    EAX设置为80000007H并读取EDX的第8位。
    如果它是 0,那么你的 CPU 没问题。
    如果它是 1,那么您的 CPU 已损坏,您需要确保在全速运行 CPU 时进行配置。

    function IsTimerBroken: boolean;
    {$ifdef CPUX86}
    asm
      //Make sure RDTSC measure CPU cycles, not wall clock time.
      push ebx
      mov eax,$80000007  //Has TSC Invariant support?
      cpuid
      pop ebx
      xor eax,eax        //Assume no
      and edx,$10        //test TSC_invariant bit
      setnz al           //if set, return true, your PC is broken.
    end;
    {$endif}
      //Make sure RDTSC measure CPU cycles, not wall clock time.
    {$ifdef CPUX64}
    asm
      mov r8,rbx
      mov eax,$80000007  //TSC Invariant support?
      cpuid
      mov rbx,r8
      xor eax,eax
      and edx,$10 //test bit 8
      setnz al
    end;
    {$endif}
    

    如何解决乱序执行问题

    见:http://www.intel.de/content/dam/www/public/us/en/documents/white-papers/ia-32-ia-64-benchmark-code-execution-paper.pdf

    使用以下代码:

    function RDTSC: int64;
    {$IFDEF CPUX64}
    asm
      {$IFDEF AllowOutOfOrder}
      rdtsc
      {$ELSE}
      rdtscp        // On x64 we can use the serializing version of RDTSC
      push rbx      // Serialize the code after, to avoid OoO sneaking in
      push rax      // later instructions before the RDTSCP runs.
      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
      {$ENDIF}
      shl rdx,32
      or rax,rdx
      {$ELSE}
    {$IFDEF CPUX86}
    asm
      {$IFNDEF AllowOutOfOrder}
      xor eax,eax
      push ebx
      cpuid         // On x86 we can't assume the existance of RDTSP
      pop ebx       // so use CPUID to serialize
      {$ENDIF}
      rdtsc
      {$ELSE}
    error!
    {$ENDIF}
    {$ENDIF}
    end;
    

    如何在损坏的 CPU 上运行 RDTSC

    诀窍是强制 CPU 以 100% 运行。
    这通常通过多次运行示例代码来完成。
    我通常使用 1.000.000 开头。
    然后,我将这 100 万次运行计时 10 倍,并采用这些尝试中最短的时间。

    与理论时间的比较表明,这给出了非常准确的结果。

    【讨论】:

    • 还有一个特性位是 TSC 在hlt 睡眠状态期间不会停止,这也使它无法用作时间源。 Linux /proc/cpuinfo 将此称为nonstop_tsc。由于乱序执行,使用rdtsc 对极短的指令序列进行计时也是有问题的。 rdtscp 可以为此提供帮助,但其他用途可能需要完整的序列化指令,以确保 rdtsc 指令不会通过其他 insn,并且其他 insn 不会通过它。对于分析,请使用性能计数器。
    • @PeterCordes Perf 计数器很烂。这就是为什么我们需要rdtsc 为什么它被破坏对我来说是个谜。添加一个与主时钟同步/不同步的额外计时器会杀死英特尔吗?
    • 我通常没有遇到问题,将我的微基准放在一个足够大的循环中以使用性能计数器。对于非常短的序列,您可以使用 IACA 或手动 uop 计数(使用 Agner Fog 的表格和 uarch 指南)来估计吞吐量/延迟/融合域 uop 计数。我想拥有一个真正的周期计数器会很好,我不能不同意。 IDK 实施起来会有多昂贵。可能不是很。但是,如果我必须选择,我会选择低开销的高精度时间源。
    • @PeterCordes,是的,但如果我想知道使用的周期。我只是使用 RDTSCP 确保 CPU 已完全征税。这样我就可以在 2 个 CPU 周期内获得计时。
    • 相关:How to get the CPU cycle count in x86_64 from C++? 有关于与 CPUID 相关的 CPUID 功能位的详细信息,以及有关其行为方式的其他内容。 (包括lfence序列化它和定时区域之间的管道)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    相关资源
    最近更新 更多