假设我有两个最初不在缓存中的数组a
和b
。我有一个循环递增i
计算b[a[i]]
的某些函数。 gcc是否能够插入必要的预取,以便在我们深入循环后b[a[i]]
将在缓存中?
答案 0 :(得分:1)
没有gcc不这样做(至少对于现代的x86-64架构而言)。例如,使用-O2
编译,以下简单代码:
double calc(int *add, double *ptr, int N){
double res=0.0;
for(int i=0;i<N;i++)
res+=1.0/ptr[add[i]];
return res;
}
导致汇编程序没有预取,这里只有循环the whole code on godbolt.org:
...
.L3:
movslq (%rdi), %rax
movapd %xmm2, %xmm1
addq $4, %rdi
cmpq %rdi, %rdx
divsd (%rsi,%rax,8), %xmm1
addsd %xmm1, %xmm0
jne .L3
rep ret
...
但是,gcc通常不会对直接访问进行任何预取:
double calc(double *ptr, int N){
double res=0;
for(int i=0;i<N;i++)
res+=1.0/ptr[i];
return res;
}
结果循环(the whole assembler here):
...
.L3:
movapd %xmm2, %xmm1
addq $8, %rdi
divsd -8(%rdi), %xmm1
cmpq %rax, %rdi
addsd %xmm1, %xmm0
jne .L3
rep ret
...
通常,gcc不会破坏缓存/预取并将其留给硬件。
正如您正确指出的那样,-fprefetch-loop-arrays
会提示gcc预取数据(whole assembler):
.L4:
movapd %xmm1, %xmm2
addl $8, %edx
prefetcht0 (%rax) ;!!! HERE PREFETCH FOR FUTURE
addq $64, %rax
divsd -160(%rax), %xmm2
addsd %xmm2, %xmm0
movapd %xmm1, %xmm2
divsd -152(%rax), %xmm2
addsd %xmm0, %xmm2
movapd %xmm1, %xmm0
divsd -144(%rax), %xmm0
addsd %xmm0, %xmm2
movapd %xmm1, %xmm0
divsd -136(%rax), %xmm0
addsd %xmm2, %xmm0
movapd %xmm1, %xmm2
divsd -128(%rax), %xmm2
addsd %xmm2, %xmm0
movapd %xmm1, %xmm2
divsd -120(%rax), %xmm2
addsd %xmm0, %xmm2
movapd %xmm1, %xmm0
divsd -112(%rax), %xmm0
addsd %xmm0, %xmm2
movapd %xmm1, %xmm0
divsd -104(%rax), %xmm0
cmpl %ecx, %edx
addsd %xmm2, %xmm0
jne .L4
然而,这并没有使程序更快 - 硬件足够聪明,可以自己预取数据而无需编译器的提示。不确定哪个功能对此负责,我的猜测是out-of-order-execution。
您提出了一个问题,即没有足够的“工作”与提取数据交错。这是事实,但即使是像
这样的功能double calc(double *ptr, int N){
double res=0;
for(int i=0;i<N;i++){
res+=1.0/ptr[i]*ptr[i]/ptr[i]*ptr[i]/ptr[i]*ptr[i];
}
return res;
}
最后一个问题:-fprefetch-loop-arrays
和第一个例子中的间接访问会发生什么?我们可以轻松see,只预取add
- 数组,而不是ptr
- 数组:
.L4:
movslq -40(%rax), %r8
movapd %xmm1, %xmm2
prefetcht0 (%rax) ;HERE rax CORRESPONDS TO add !!
addl $16, %ecx
addq $64, %rax
divsd (%rsi,%r8,8), %xmm2
movslq -100(%rax), %r8
addsd %xmm2, %xmm0
movapd %xmm1, %xmm2
divsd (%rsi,%r8,8), %xmm2
movslq -96(%rax), %r8
addsd %xmm0, %xmm2
movapd %xmm1, %xmm0
divsd (%rsi,%r8,8), %xmm0
movslq -92(%rax), %r8
addsd %xmm0, %xmm2
movapd %xmm1, %xmm0
divsd (%rsi,%r8,8), %xmm0
movslq -88(%rax), %r8
addsd %xmm2, %xmm0
movapd %xmm1, %xmm2
divsd (%rsi,%r8,8), %xmm2
movslq -84(%rax), %r8
addsd %xmm2, %xmm0
movapd %xmm1, %xmm2
divsd (%rsi,%r8,8), %xmm2
movslq -80(%rax), %r8
addsd %xmm0, %xmm2
movapd %xmm1, %xmm0
divsd (%rsi,%r8,8), %xmm0
movslq -76(%rax), %r8
addsd %xmm0, %xmm2
movapd %xmm1, %xmm0
divsd (%rsi,%r8,8), %xmm0
movslq -72(%rax), %r8
addsd %xmm2, %xmm0
movapd %xmm1, %xmm2
divsd (%rsi,%r8,8), %xmm2
movslq -68(%rax), %r8
addsd %xmm2, %xmm0
movapd %xmm1, %xmm2
divsd (%rsi,%r8,8), %xmm2
movslq -64(%rax), %r8
addsd %xmm0, %xmm2
movapd %xmm1, %xmm0
divsd (%rsi,%r8,8), %xmm0
movslq -60(%rax), %r8
addsd %xmm0, %xmm2
movapd %xmm1, %xmm0
divsd (%rsi,%r8,8), %xmm0
movslq -56(%rax), %r8
addsd %xmm2, %xmm0
movapd %xmm1, %xmm2
divsd (%rsi,%r8,8), %xmm2
movslq -52(%rax), %r8
addsd %xmm2, %xmm0
movapd %xmm1, %xmm2
divsd (%rsi,%r8,8), %xmm2
movslq -48(%rax), %r8
addsd %xmm0, %xmm2
movapd %xmm1, %xmm0
divsd (%rsi,%r8,8), %xmm0
movslq -44(%rax), %r8
cmpl %r9d, %ecx
addsd %xmm0, %xmm2
movapd %xmm1, %xmm0
divsd (%rsi,%r8,8), %xmm0
addsd %xmm2, %xmm0
jne .L4