X86 如何确保RDTSC的准确性?

X86 如何确保RDTSC的准确性?,x86,x86-64,cpuid,rdtsc,X86,X86 64,Cpuid,Rdtsc,我已经了解到RDTSC可能会给出错误的读数,因此不应依赖它。 这是真的吗?如果是真的,我们可以做些什么呢?非常旧的CPU有一个精确的RDTSC 问题 但是,较新的CPU有问题。 工程师们认为RDTSC非常适合计时。 但是,如果CPU限制了频率,RDTSC就无法告诉时间。 前面提到的脑残工程师决定“修复”这个问题,让TSC始终以相同的频率运行,即使CPU速度减慢 这有一个“优势”,即TSC可用于告知经过的(挂钟)时间。然而,它使TSC在分析时变得毫无用处 如何判断CPU是否损坏 通过读取CPUID

我已经了解到RDTSC可能会给出错误的读数,因此不应依赖它。

这是真的吗?如果是真的,我们可以做些什么呢?

非常旧的CPU有一个精确的RDTSC

问题
但是,较新的CPU有问题。
工程师们认为RDTSC非常适合计时。
但是,如果CPU限制了频率,RDTSC就无法告诉时间。
前面提到的脑残工程师决定“修复”这个问题,让TSC始终以相同的频率运行,即使CPU速度减慢

这有一个“优势”,即TSC可用于告知经过的(挂钟)时间。然而,它使TSC在分析时变得毫无用处

如何判断CPU是否损坏
通过读取CPUID中的
TSC\u不变量
位,可以判断CPU是否正常

AEX
设置为8000000小时,并读取
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}
如何解决无序执行问题
见:

使用以下代码:

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倍,并取这些尝试中最短的时间


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

hlt
sleep状态下,TSC还有一个不停止的特性位,这也使得它不能作为时间源使用。Linux/proc/cpuinfo调用这个
nonstop\u tsc
。使用
rdtsc
对极短的指令序列进行计时也是有问题的,因为执行顺序错误
rdtscp
可以帮助实现这一点,但其他用途可能需要一条完整的串行化指令,以确保
rdtsc
指令不会通过其他INSN,并且其他INSN不会通过它。对于分析,请使用性能计数器。@PeterCordes性能计数器。这就是为什么我们需要rdtsc的原因,它为什么被破坏对我来说是个谜。如果增加一个与主时钟同步/不同步的额外计时器,会不会扼杀英特尔?我通常不会有问题将我的微基准放在一个大到足以使用性能计数器的循环中。对于非常短的序列,您可以使用IACA或手动uop计数(使用Agner Fog的表和uarch指南)来估计吞吐量/延迟/融合域uop计数。我想有一个真正的循环计数器会很好,我不能不同意。IDK实施的成本有多高。可能不太好。如果我必须选择,我会选择低开销高精度的时间源。@PeterCordes,是的,但如果我想知道使用的周期。我只是使用RDTSCP来确保cpu是完全被占用的。这样我可以在2个CPU周期内获得计时。