我知道rdtsc
将处理器时间戳计数器的当前值加载到两个寄存器中:EDX和EAX。为了在x86上实现它,我需要这样做(假设使用Linux):
unsigned long lo, hi;
asm( "rdtsc" : "=a" (lo), "=d" (hi));
return lo;
对于x86_x64:
unsigned long lo, hi;
asm( "rdtsc" : "=a" (lo), "=d" (hi) );
return( lo | (hi << 32) );
为什么?有人能向我解释一下吗?
RDTSC总是在EDX和EAX中将其64位结果拆分为高低两半,即使在64位模式下也是如此(请参阅手册),不幸的是,它没有将64位TSC打包为RAX。这就是为什么在asm语句之后需要额外的工作。
要从中生成一个64位整数,需要将hi
作为unsigned long
的一部分移位到它所属的位置。lo
已经在正确的位置,写入这些32位寄存器会将两个寄存器的高位置零,因此我们可以将(移位的)一半进行"或"运算,而不必与低一半进行"与"运算。
在x86-64 Linux中,unsigned long
是64位类型,因此内核实际上使用RDTSC返回值的两半。
32位版本更简单的唯一原因是内核通过丢弃高半部分将结果截断为32位。如果您确实希望在32位模式下使用64位TSC,那么相同的C源也可以在那里工作(使用uint64_t
或unsigned long long
),尽管它不会编译为移位和or指令。编译器只知道它有一个64位整数,它的一半在EDX和EAX中。
另请参阅如何从C++获得x86_64中的CPU周期计数?-为了真正使用,别忘了制作这些asm volatile
。否则,编译器可以假设重复执行会产生相同的输出,例如优化后的end-start
=0。
区别不在于rdtsc
,而在于Linux内核想用它做什么
在32位中,它返回一个32位的值。所以eax中的值已经足够好了
在64位中,它返回一个64位值。因此,它需要将两个寄存器中的值组合起来。