gcc可以通过两个数组预取吗? (间接预取)

时间:2018-05-03 01:54:37

标签: caching gcc optimization prefetch

假设我有两个最初不在缓存中的数组ab。我有一个循环递增i计算b[a[i]]的某些函数。 gcc是否能够插入必要的预取,以便在我们深入循环后b[a[i]]将在缓存中?

1 个答案:

答案 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;
}

nothing changes

最后一个问题:-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