通过引用和值

时间:2015-10-17 15:29:29

标签: c++ loops gcc assembly

我有一个简单的函数来计算产品 两个双数组:

#include <stdlib.h>
#include <emmintrin.h>

struct S {
    double *x;
    double *y;
    double *z;
};

void f(S& s, size_t n) {
    for (int i = 0; i < n; i += 2) {
        __m128d xs = _mm_load_pd(&s.x[i]);
        __m128d ys = _mm_load_pd(&s.y[i]);
        _mm_store_pd(&s.z[i], _mm_mul_pd(xs, ys) );
    }
    return;
}

int main(void) {
    S s;
    size_t size = 4;
    posix_memalign((void **)&s.x, 16, sizeof(double) * size);
    posix_memalign((void **)&s.y, 16, sizeof(double) * size);
    posix_memalign((void **)&s.z, 16, sizeof(double) * size);
    f(s, size);
    return 0;
}

请注意,函数f的第一个参数是通过引用传入的。 让我们看看f()的结果汇编(我删除了一些不相关的 件,插入注释并放置一些标签):

$ g++ -O3 -S asmtest.cpp 


        .globl      _Z1fR1Sm
_Z1fR1Sm:
        xorl        %eax, %eax
        testq       %rsi, %rsi
        je  .L1
.L5:
        movq        (%rdi), %r8             # array x   (1)
        movq        8(%rdi), %rcx           # array y   (2)
        movq        16(%rdi), %rdx          # array z   (3)
        movapd      (%r8,%rax,8), %xmm0     # load x[0]
        mulpd       (%rcx,%rax,8), %xmm0    # multiply x[0]*y[0]
        movaps      %xmm0, (%rdx,%rax,8)    # store to y
        addq        $2, %rax                # and loop
        cmpq        %rax, %rsi
        ja  .L5

请注意,数组x,y,z的地址被加载到通用目的中 在每次迭代中注册,参见语句(1),(2),(3)。为什么gcc不移动 循环外的这些说明?

现在制作一个结构的本地副本(不是深层副本):

void __attribute__((noinline)) f(S& args, size_t n) {
    S s = args;
    for (int i = 0; i < n; i += 2) {
        __m128d xs = _mm_load_pd(&s.x[i]);
        __m128d ys = _mm_load_pd(&s.y[i]);
        _mm_store_pd(&s.z[i], _mm_mul_pd(xs, ys) );
    }
    return;
}

大会:

_Z1fR1Sm:
.LFB525:
        .cfi_startproc
        xorl        %eax, %eax
        testq       %rsi, %rsi
        movq        (%rdi), %r8     # (1)
        movq        8(%rdi), %rcx   # (2)
        movq        16(%rdi), %rdx  # (3)
        je  .L1
.L5:
        movapd      (%r8,%rax,8), %xmm0
        mulpd       (%rcx,%rax,8), %xmm0
        movaps      %xmm0, (%rdx,%rax,8)
        addq        $2, %rax
        cmpq        %rax, %rsi
        ja  .L5
.L1:
        rep ret

请注意,与之前的代码不同, load(1),(2),(3)现在在循环之外。

我很感激为什么这两个组装的解释 代码不同。内存别名是否与此相关? 感谢。

$ gcc --version gcc(Debian 5.2.1-21)5.2.1 20151003

1 个答案:

答案 0 :(得分:2)

是的,gcc正在重复加载s.xs.y循环的每次迭代,因为gcc不知道某些&s.z[i]别名的i是{{1}的一部分通过引用传递给S的对象。

使用gcc 5.2.0,将f(S&, size_t)应用于__restrict__,将S::z引用参数应用于s,即:

f()

..导致gcc生成:

struct S {
    double *x;
    double *y;
    double *__restrict__ z;
};

void f(S&__restrict__ s, size_t n) {
    for (int i = 0; i < n; i += 2) {
        __m128d xs = _mm_load_pd(&s.x[i]);
        __m128d ys = _mm_load_pd(&s.y[i]);
        _mm_store_pd(&s.z[i], _mm_mul_pd(xs, ys));
    }
    return;
}

使用Apple Clang 700.1.76时,只需要__Z1fR1Sm: LFB518: testq %rsi, %rsi je L1 movq (%rdi), %r8 xorl %eax, %eax movq 8(%rdi), %rcx movq 16(%rdi), %rdx .align 4,0x90 L4: movapd (%r8,%rax,8), %xmm0 mulpd (%rcx,%rax,8), %xmm0 movaps %xmm0, (%rdx,%rax,8) addq $2, %rax cmpq %rax, %rsi ja L4 L1: ret 引用__restrict__

s