Caching gcc可以通过两个阵列进行预取吗?(间接预取)
假设我有两个数组Caching gcc可以通过两个阵列进行预取吗?(间接预取),caching,gcc,optimization,prefetch,Caching,Gcc,Optimization,Prefetch,假设我有两个数组a和b,它们最初不在缓存中。我有一个循环递增I计算b[a[I]]的一些函数。gcc是否能够插入必要的预取,以便b[a[i]]在我们深入循环后进入缓存 没有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]];
a
和b
,它们最初不在缓存中。我有一个循环递增I
计算b[a[I]]
的一些函数。gcc是否能够插入必要的预取,以便b[a[i]]
在我们深入循环后进入缓存 没有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;
}
但是,gcc通常不会对直接访问进行任何预取:
double calc(double *ptr, int N){
double res=0;
for(int i=0;i<N;i++)
res+=1.0/ptr[i];
return res;
}
通常,gcc不会干扰缓存/预取,而是将其留给硬件
正如您正确指出的,-fprefetch循环数组将提示gcc预取数据():
然而,这并不能使程序更快——硬件足够聪明,可以在没有编译器提示的情况下自行预取数据。我不确定是哪个功能导致了这种情况,我猜是最重要的
您提出了一个问题,即没有足够的“工作”可以与数据的获取交叉进行。这是真的,但即使对于像
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;
}
这是一个非常彻底的回答,谢谢。出于某种原因,我认为gcc在加入预取指令时更具攻击性!然而,我确实担心这些例子是有偏见的——对获取的内存所做的工作很少,所以预取也帮不上忙。如果您对每一行缓存都进行了足够的计算,以便有足够的时间获取新的缓存行,则可以完全避免未命中!我想知道gcc是否会抓住这种情况?而且,我相信这个标志可能会改变行为:-fprefetch循环数组
。你说得对,但在现代cpu上,插入预取并不能使程序更快。不过,对于不太聪明/较旧的体系结构来说,这可能很重要。有关更多详细信息,请参见我的更新。
...
.L3:
movapd %xmm2, %xmm1
addq $8, %rdi
divsd -8(%rdi), %xmm1
cmpq %rax, %rdi
addsd %xmm1, %xmm0
jne .L3
rep ret
...
.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
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;
}
.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