我正在尝试准确测量对不同缓存级别的内存访问,并提出了此代码进行探测:
__asm__ __volatile__(
"xor %%eax, %%eax \n"
"xor %%edi, %%edi \n"
"xor %%edx, %%edx \n"
/* time measurement */
"lfence \n"
"rdtsc \n"
"shl $32, %%rdx \n"
"or %%rdx, %%rax \n"
"movq %%rax, %%rdi \n"
/* memory access */
"movq (%%rsi), %%rbx\n"
/* time measurement */
"rdtscp \n"
"shl $32, %%rdx \n"
"or %%rdx, %%rax \n"
"movq %%rax, %%rsi \n"
"cpuid \n"
: /* output operands */
"=S"(t2), "=D"(t1)
: /* input operands */
"S" (mem)
: /* clobber description */
"ebx", "ecx", "edx", "cc", "memory"
);
然而,L1和L2缓存访问仅相差8个周期且结果波动很大,因此我决定检查周围代码(除了实际内存访问)对时间的影响:
__asm__ __volatile__(
"xor %%eax, %%eax \n"
"xor %%edi, %%edi \n"
"xor %%edx, %%edx \n"
/* time measurement */
"lfence \n"
"rdtsc \n"
"shl $32, %%rdx \n"
"or %%rdx, %%rax \n"
"movq %%rax, %%rdi \n"
/* memory access */
//"movq (%%rsi), %%rbx\n"
/* time measurement */
"rdtscp \n"
"shl $32, %%rdx \n"
"or %%rdx, %%rax \n"
"movq %%rax, %%rsi \n"
"cpuid \n"
: /* output operands */
"=S"(t2), "=D"(t1)
: /* input operands */
"S" (mem)
: /* clobber description */
"ebx", "ecx", "edx", "cc", "memory"
);
结果如下:
./cache_testing
From Memory: 42
From L3: 46
From L2: 40
From L1: 38
./cache_testing
From Memory: 40
From L3: 38
From L2: 36
From L1: 40
我知道我目前没有达到目的不同的高速缓存级别,但我想知道为什么在缺少内存访问的情况下,时间如此波动。 代码作为具有最高优先级的SCHED_FIFO运行,固定到一个CPU,运行时不应调度。 任何人都可以告诉我,我是否可以以任何方式改进我的代码,从而改善结果?
答案 0 :(得分:1)
要修正测量代码,您需要将空设置作为基线进行测量,以减去测量开销。
另外请记住,TSC会计算参考周期,而不是核心时钟周期,因此要确保CPU始终以相同的速度运行。 (例如,禁用turbo并使用预热循环使CPU达到最高速度,如果不进行超频,则TSC计数应与核心周期匹配。)
这可能解释了波动。
我通常用perf计数器测量东西,而不是RDTSC。
但我认为你应该在第一个RDTSC之前使用序列化指令(如CPUID)。在第二个RDTSC之后使用CPUID可能没用。 rdstcp
for the second measurement is useful,因为它表示时间戳来自加载执行后。 (手册说“执行”; IDK,如果这意味着“退休”或者只是由负载端口执行。)
所以IIRC,你最好的选择是:
# maybe set eax to something before CPUID
cpuid
rdtsc
shl $32, %%rdx
lea (%%rax, %%rdx), %%rsi
... code under test
# CPUID here, too, if you can only use rdtsc instead of rdtscp
rdtscp
shl $32, %%rdx
or %%rdx, %%rax
sub %%rsi, %%rax
# time difference in RAX
如果被测代码与shift / LEA竞争相同的ALU端口,则只能mov
第一个RDTSC结果的低32位到另一个寄存器。而不是处理高32。如果假设时间戳的差异远小于2 ^ 32,则不需要任何计数的高32位。
我已经读过,在现代CPU上测量像这样的微小序列可以通过性能计数器比使用TSC更好。 Agner Fog's test programs包括从程序内部使用perf计数器来测量某些内容的代码。这可以让您测量核心周期,无论是turbo还是非turbo,因为核心时钟周期性能计数器实际上每个物理时钟周期计数一次。