【问题标题】:how to auto vectorization array comparison function如何自动矢量化数组比较功能
【发布时间】:2017-02-12 21:50:44
【问题描述】:

这是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 类似于我的第一个问题。在那个问题中,向量是一个原始指针,因此段错误是可能的,但这不是问题。因此,AFAIK 重新排序比较操作在这里是安全的,但在那里不安全。结论可能是一样的。

【问题讨论】:

    标签: c gcc vector


    【解决方案1】:

    自动向量化真的很喜欢归约操作,所以诀窍就是把它变成归约。

    #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
    

    【讨论】:

      【解决方案2】:

      所以你的循环很小而且它是递归的:迭代 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
      

      最后一点,在优化代码并启用外循环后,我看到了一些优化。您是否更改了编译选项?

      【讨论】:

      • 有和没有外循环,我使用的编译器标志是“-O3 -fopt-info-vec-optimized”。当然 gcc 进行了许多优化,但特定于矢量化没有优化的循环,外部循环未注释,如 fopt-info 报告的那样。另外,我的白痴检查浮点矢量化的指南是寻找一堆 *ps 指令,一旦包含外循环就没有了
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-27
      • 1970-01-01
      相关资源
      最近更新 更多