2016-03-05 100 views

回答

4

非常旧的CPU的RDTSC是准确的。

问题
但是新的CPU有问题。
工程师们认为RDTSC对于讲述时间会很有帮助。
但是,如果CPU节制频率RDTSC是无用的告诉时间。
上述braindead工程师决定通过让TSC始终以相同的频率运行,即使CPU速度变慢来“解决”这个问题。

这具有TSC可用于告诉已过时(挂钟)时间的'优势'。但是它使得TSC 无用 对分析的用处不大。

如何判断你的CPU不破
如果你可以告诉你的CPU是通过读取TSC_invariant位在CPUID罚款。

设置AEX为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次,并采取这些尝试的最低时间。

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

+0

TSC还有一个特性位,它在hlt睡眠状态期间不会停止,这也使得它不能用作时间源。 Linux/proc/cpuinfo调用这个'nonstop_tsc'。由于乱序执行,使用'rdtsc'来定时极短的指令序列也是有问题的。 'rdtscp'可以提供帮助,但其他用途可能需要一个完整的序列化指令来确保'rdtsc'指令不能通过其他insn,而其他insns不会通过它。为了分析,使用perf计数器。 –

+0

@PeterCordes Perf计数器吸。这就是为什么我们需要'rdtsc'为什么它被打破对我来说是一个谜。它会杀死英特尔添加一个与主时钟运行/不同步的额外定时器吗? – Johan

+0

我通常没有把我的microbenchmark放入一个足够大的循环来使用perf计数器的问题。对于非常短的序列,您可以使用IACA或手动计数(使用Agner Fog的表格和uarch指南)来估计吞吐量/延迟/融合域计数。我想这将是一个真正的周期柜台,我不能不同意。 IDK实施起来会花多少钱。可能不是很。但是,如果我不得不选择低开销的高精度时间源,那么我会选择它。 –