使用 RDTSC 测量时差 - 结果太大
Measuring time difference using RDTSC - results too large
我正在尝试计算 运行 单个 ASM 指令所需的 CPU 周期数。为此,我创建了这个函数:
measure_register_op:
# Calculate time of required for movl operation
# function setup
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
xor %edi, %edi
# first time measurement
xorl %eax, %eax
cpuid # sync of threads
rdtsc # result in edx:eax
# we are measuring instuction below
movl %eax, %edi
# second time measurement
cpuid # sync of threads
rdtsc # result in edx:eax
# time difference
sub %eax, %edi
# move to EAX. Value of EAX is what function returns
movl %edi, %eax
# End of function
popl %edi
popl %ebx
mov %ebp, %esp
popl %ebp
ret
我在 *.c 文件中使用它:
extern unsigned int measure_register_op();
int main(void)
{
for (int a = 0; a < 10; a++)
{
printf("Instruction took %u cycles \n", measure_register_op());
}
return 0;
}
问题是:我看到的值太大了。我现在得到 3684414156
。这里会出什么问题?
编辑:
从 EBX 更改为 EDI,但结果仍然相似。它必须与 rdtsc 本身有关。在调试器中,我可以看到第二个测量结果为 0x7f61e078,第一个为 0x42999940,减法后仍然给出大约 1019758392
编辑:
这是我的生成文件。也许我编译不正确:
compile: measurement.s measurement.c
gcc -g measurement.s measurement.c -o ./build/measurement -m32
编辑:
这是我看到的确切结果:
Instruction took 4294966680 cycles
Instruction took 4294966696 cycles
Instruction took 4294966688 cycles
Instruction took 4294966672 cycles
Instruction took 4294966680 cycles
Instruction took 4294966688 cycles
Instruction took 4294966688 cycles
Instruction took 4294966696 cycles
Instruction took 4294966688 cycles
Instruction took 4294966680 cycles
cpuid
破坏了 ebx
和许多其他寄存器。您需要避免在此处使用 cpuid
或将值保存在不会被破坏的地方。
在您的更新版本中,不会破坏开始时间(@R. 指出的错误):
sub %eax, %edi
正在计算 start - end
。这是一个负数,即刚好低于 2^32 的巨大无符号数。如果您要使用 %u
,请习惯在调试时将其输出解释回 bit-pattern。
你想要end - start
.
顺便说一句,使用lfence
;它的效率明显高于 cpuid
。它保证在 Intel 上序列化指令 execution(不会像完整的序列化指令那样刷新存储缓冲区)。在 AMD CPUs with Spectre mitigation enabled.
上也是安全的
另请参阅 http://akaros.cs.berkeley.edu/lxr/akaros/kern/arch/x86/rdtsc_test.c 以了解序列化 RDTSC and/or RDTSCP 的一些不同方法。
另请参阅 Get CPU cycle count? 以了解有关 RDTSC 的更多信息,尤其是它不计算核心时钟周期,仅计算参考周期。所以idle/turbo会影响你的结果。
此外,一条指令的成本不是 one-dimensional。 像那样用 RDTSC 对单个指令计时并不是特别有用。有关如何测量单个指令的 throughput/latency/uops 的更多信息,请参阅 。
RDTSC 可用于为整个循环或更长的指令序列计时,大于 CPU.
的 OoO 执行 window
我正在尝试计算 运行 单个 ASM 指令所需的 CPU 周期数。为此,我创建了这个函数:
measure_register_op:
# Calculate time of required for movl operation
# function setup
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
xor %edi, %edi
# first time measurement
xorl %eax, %eax
cpuid # sync of threads
rdtsc # result in edx:eax
# we are measuring instuction below
movl %eax, %edi
# second time measurement
cpuid # sync of threads
rdtsc # result in edx:eax
# time difference
sub %eax, %edi
# move to EAX. Value of EAX is what function returns
movl %edi, %eax
# End of function
popl %edi
popl %ebx
mov %ebp, %esp
popl %ebp
ret
我在 *.c 文件中使用它:
extern unsigned int measure_register_op();
int main(void)
{
for (int a = 0; a < 10; a++)
{
printf("Instruction took %u cycles \n", measure_register_op());
}
return 0;
}
问题是:我看到的值太大了。我现在得到 3684414156
。这里会出什么问题?
编辑:
从 EBX 更改为 EDI,但结果仍然相似。它必须与 rdtsc 本身有关。在调试器中,我可以看到第二个测量结果为 0x7f61e078,第一个为 0x42999940,减法后仍然给出大约 1019758392
编辑: 这是我的生成文件。也许我编译不正确:
compile: measurement.s measurement.c
gcc -g measurement.s measurement.c -o ./build/measurement -m32
编辑: 这是我看到的确切结果:
Instruction took 4294966680 cycles
Instruction took 4294966696 cycles
Instruction took 4294966688 cycles
Instruction took 4294966672 cycles
Instruction took 4294966680 cycles
Instruction took 4294966688 cycles
Instruction took 4294966688 cycles
Instruction took 4294966696 cycles
Instruction took 4294966688 cycles
Instruction took 4294966680 cycles
cpuid
破坏了 ebx
和许多其他寄存器。您需要避免在此处使用 cpuid
或将值保存在不会被破坏的地方。
在您的更新版本中,不会破坏开始时间(@R. 指出的错误):
sub %eax, %edi
正在计算 start - end
。这是一个负数,即刚好低于 2^32 的巨大无符号数。如果您要使用 %u
,请习惯在调试时将其输出解释回 bit-pattern。
你想要end - start
.
顺便说一句,使用lfence
;它的效率明显高于 cpuid
。它保证在 Intel 上序列化指令 execution(不会像完整的序列化指令那样刷新存储缓冲区)。在 AMD CPUs with Spectre mitigation enabled.
另请参阅 http://akaros.cs.berkeley.edu/lxr/akaros/kern/arch/x86/rdtsc_test.c 以了解序列化 RDTSC and/or RDTSCP 的一些不同方法。
另请参阅 Get CPU cycle count? 以了解有关 RDTSC 的更多信息,尤其是它不计算核心时钟周期,仅计算参考周期。所以idle/turbo会影响你的结果。
此外,一条指令的成本不是 one-dimensional。 像那样用 RDTSC 对单个指令计时并不是特别有用。有关如何测量单个指令的 throughput/latency/uops 的更多信息,请参阅
RDTSC 可用于为整个循环或更长的指令序列计时,大于 CPU.
的 OoO 执行 window