非常旧的 CPU 有一个准确的 RDTSC。
然而,较新的 CPU 有问题。
工程师们认为 RDTSC 非常适合告诉时间。
但是,如果 CPU 限制频率,则 RDTSC 对告诉时间毫无用处。
前面提到的脑残工程师决定通过让 TSC 始终以相同的频率运行来“解决”这个问题,即使 CPU 速度变慢。
这具有“优势”,即 TSC 可用于告知已用(挂钟)时间。然而,它使 TSC对分析没有用处。
您可以通过读取TSC_invariantCPUID 中的位来判断您的 CPU 是否正常。
设置EAX为 80000007H 并读取EDX.
如果它是 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}
Run Code Online (Sandbox Code Playgroud)
请参阅: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;
Run Code Online (Sandbox Code Playgroud)
诀窍是强制 CPU 以 100% 运行。
这通常通过多次运行示例代码来完成。
我通常使用 1.000.000 开始。
然后我将这 100 万次运行计时 10 倍,并使用这些尝试中最短的时间。
与理论时序的比较表明,这给出了非常准确的结果。