gcc 如何自动矢量化数组比较函数

kxkpmulp  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(142)

这是post的后续版本。免责声明:我已经完成了零配置,甚至没有应用程序,这纯粹是为了让我了解更多关于矢量化的信息。
我的代码如下。我在一台i3 m370机器上用gcc 4.9.4编译。第一个循环如我所期望的那样向量化了。但是第二个循环检查temp的每个元素并不是向量化的AFAICT,而是所有的“andb”指令。我期望它能用类似于_mm_test_all_ones的东西向量化。这个循环怎么也能向量化呢?第二个问题,我真的希望这是一个更大的循环的一部分。如果我取消注解下面的内容,什么都不会得到矢量化。我怎么才能也得到矢量化呢?

#define ARR_LENGTH 4096
#define block_size 4
typedef float afloat __attribute__ ((__aligned__(16)));

char all_equal_2(afloat *a, afloat *b){
    unsigned int i, j;
    char r = 1;
    unsigned int temp[block_size] __attribute__((aligned(16)));
    //for (i=0; i<ARR_LENGTH; i+=block_size){

        for (j = 0; j < block_size; ++j) {
            temp[j] = (*a) == (*b);
            a++;
            b++;
        }

        for (j=0; j<block_size; j++){
            r &= temp[j];
        }

        /*if (r == 0){
            break;
        }
    }*/
    return r;
}

并由此产生装配的关键部分:

.cfi_startproc
    movaps  (%rdi), %xmm0
    cmpeqps (%rsi), %xmm0
    movdqa  .LC0(%rip), %xmm1
    pand    %xmm0, %xmm1
    movaps  %xmm1, -24(%rsp)
    movl    -24(%rsp), %eax
    andl    $1, %eax
    andb    -20(%rsp), %al
    andb    -16(%rsp), %al
    andb    -12(%rsp), %al
    ret
    .cfi_endproc

更新:这个post与我的第一个问题类似。在那个问题中,向量是一个原始指针,所以可能出现segfault,但在这里这不是问题。因此AFAIK在这里重新排序比较操作是安全的,但在那里不是。结论可能是一样的。

6ju8rftf

6ju8rftf1#

自动矢量化非常喜欢约简操作,所以技巧是将其转化为约简。

#define ARR_LENGTH 4096
typedef float afloat __attribute__ ((__aligned__(16)));
int foo(afloat *a, afloat *b){
    unsigned int i, j;
    unsigned int result;
    unsigned int blocksize = 4;
    for (i=0; i<ARR_LENGTH; i+=blocksize){
        result = 0;
        for (j=0; j<blocksize; j++){
            result += (*a) == (*b);
            a++;
            b++;
        }
        if (result == blocksize){
            blocksize *= 2;
        } else {
            break;
        }
    }
    blocksize = ARR_LENGTH - i;
    for (i=0; i<blocksize; i++){
        result += (*a) == (*b);
        a++;
        b++;
    }
    return result == i;
}

编译成一个很好的循环:

.L3:
        movaps  (%rdi,%rax), %xmm1
        addl    $1, %ecx
        cmpeqps (%rsi,%rax), %xmm1
        addq    $16, %rax
        cmpl    %r8d, %ecx
        psubd   %xmm1, %xmm0
        jb      .L3
rta7y2nd

rta7y2nd2#

所以你的循环很小,而且是递归的:迭代N的结果被用作迭代N+1的输入。如果你改变你的第二个循环以允许每次迭代2个操作:

char r2 = r;
    for (j=0; j<block_size/2; j+=2){
        r &= temp[j];
        r2 &=temp[j+1];
    }
    r &= r2;

您将看到输出已优化

.cfi_def_cfa_register %rbp
vmovss  (%rdi), %xmm0           ## xmm0 = mem[0],zero,zero,zero
vmovss  4(%rdi), %xmm1          ## xmm1 = mem[0],zero,zero,zero
vucomiss    (%rsi), %xmm0
sete    %al
vucomiss    4(%rsi), %xmm1
sete    %cl
andb    %al, %cl
movzbl  %cl, %eax
popq    %rbp
retq
.cfi_endproc

最后一点,代码优化了,启用了外部循环,我看到了一些优化。2你改变编译选项了吗?

相关问题