Caching 使用RDTSC和RDTSCP进行精确的内存访问时间探测?
我正试图对不同缓存级别的内存访问进行精确测量,并提出了以下探测代码:Caching 使用RDTSC和RDTSCP进行精确的内存访问时间探测?,caching,memory,assembly,x86-64,timing,Caching,Memory,Assembly,X86 64,Timing,我正试图对不同缓存级别的内存访问进行精确测量,并提出了以下探测代码: __asm__ __volatile__( "xor %%eax, %%eax \n" "xor %%edi, %%edi \n" "xor %%edx, %%edx \n" /* time measurement */ "lfence \n" "rdtsc \n"
__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"
);
然而,一级缓存和二级缓存访问仅相差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上,运行时不应调度。
有谁能告诉我,我是否可以以任何方式改进我的代码,从而改进结果吗?要修复您的测量代码,您需要测量一个空设置作为基线,以减去测量开销,这是正确的 还请记住,TSC统计的是参考周期,而不是核心时钟周期,因此要使其正常工作,您需要确保CPU始终以相同的速度运行。(例如,禁用turbo并使用预热循环使CPU达到最高速度,如果不超频,则TSC计数应与核心周期匹配。) 这或许可以解释这种波动
我通常用性能计数器测量,而不是RDTSC 但是我认为应该在第一个RDTSC之前使用序列化指令(比如CPUID)。在第二个RDTSC之后使用CPUID可能没有用处,因为它意味着时间戳来自于加载执行之后。(手册上写着“已执行”;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端口,您可以将第一个RDTSC结果的低位32转移到另一个寄存器。而不是处理高32。如果假设时间戳的差异远小于2^32,则不需要两个计数的高32位
我已经读到,在现代CPU上测量这样的微小序列,使用性能计数器可以比使用TSC做得更好。包括用于从程序内部使用性能计数器来测量某些内容的代码。这可以让您测量核心周期,而不考虑turbo或非turbo,因为核心时钟周期性能计数器实际上在每个物理时钟周期中计数一个。Intel Haswell上缓存加载->使用延迟的正确数字是L1的4c,L2的12c,根据。衡量这一点的一个好方法(特别是对于L1)是指针追踪。对于L1,只需设置一个指向自身的指针,并在循环中运行
mov(%rax),%rax
。对于L2,您需要一个不适合L1的大链表。Related有一个关于lfence+rdtsc+lfence.Update的详细答案:lfence;rdtsc会对其进行序列化,并且比CPUID更高效。