即使希望获得 RDTSC 提供的可重复、确定的时序,您也需要采取一些额外的步骤。首先,RDTSC 是not序列化指令,因此它可以乱序执行,这通常会使其在像上面这样的代码片段中毫无意义。
您通常需要使用序列化指令,然后是 RDTSC,然后是有问题的代码,另一个序列化指令,以及第二个 RDTSC。
用户模式下几乎唯一可用的序列化指令是CPUID。然而,这又增加了一个小问题:CPUID 被英特尔记录为需要不同的执行时间——前几次执行可能比其他执行慢。
因此,代码的正常时序将如下所示:
XOR EAX, EAX
CPUID
XOR EAX, EAX
CPUID
XOR EAX, EAX
CPUID ; Intel says by the third execution, the timing will be stable.
RDTSC ; read the clock
push eax ; save the start time
push edx
mov ebx, 0x1E532 // Seed // execute test sequence
shl ebx, 3
add ebx, 0x0054E9
mov value, ebx
XOR EAX, EAX ; serialize
CPUID
rdtsc ; get end time
pop ecx ; get start time back
pop ebp
sub eax, ebp ; find end-start
sbb edx, ecx
我们已经开始接近这一目标,但最后一点是在大多数编译器上使用内联代码很难处理:交叉缓存行也可能会产生一些影响,因此您通常希望强制代码与 16 对齐-字节(段落)边界。任何像样的汇编器都会支持这一点,但编译器中的内联汇编通常不会。
说了这么多,我认为你是在浪费时间。正如你可以猜到的,我已经在这个级别上做了相当多的计时,并且我很确定你所听到的完全是一个神话。事实上,所有最新的 x86 CPU 都使用一组所谓的“重命名寄存器”。长话短说,这意味着您为寄存器使用的名称实际上并不重要——CPU 有一组更大的寄存器(例如,Intel 大约有 40 个)用于实际操作,因此在 EBX 与 EAX 中输入值对 CPU 真正要在内部使用的寄存器几乎没有影响。两者都可以映射到任何重命名寄存器,主要取决于该指令序列开始时哪些重命名寄存器恰好是空闲的。