GCC:两个类似环路之间的差异矢量(GCC: vectorization difference be

2019-06-27 08:02发布

当编译gcc -O3 ,为什么下面的循环不是矢量化(自动):

#define SIZE (65536)

int a[SIZE], b[SIZE], c[SIZE];

int foo () {
  int i, j;

  for (i=0; i<SIZE; i++){
    for (j=i; j<SIZE; j++) {
      a[i] = b[i] > c[j] ? b[i] : c[j];
    }
  }
  return a[0];
}

当以下一个呢?

#define SIZE (65536)

int a[SIZE], b[SIZE], c[SIZE];

int foov () {
  int i, j;

  for (i=0; i<SIZE; i++){
    for (j=i; j<SIZE; j++) {
      a[i] += b[i] > c[j] ? b[i] : c[j];
    }
  }
  return a[0];
}

唯一的区别是在内部循环的表达式的结果是否被分配到一个[I]中,或添加到[i]中

为了参考-ftree-vectorizer-verbose=6给出了第一(非向量化)环以下的输出。

v.c:8: note: not vectorized: inner-loop count not invariant.
v.c:9: note: Unknown alignment for access: c
v.c:9: note: Alignment of access forced using peeling.
v.c:9: note: not vectorized: live stmt not supported: D.2700_5 = c[j_20];

v.c:5: note: vectorized 0 loops in function.

而对于向量化循环相同的输出是:

v.c:8: note: not vectorized: inner-loop count not invariant.
v.c:9: note: Unknown alignment for access: c
v.c:9: note: Alignment of access forced using peeling.
v.c:9: note: vect_model_load_cost: aligned.
v.c:9: note: vect_model_load_cost: inside_cost = 1, outside_cost = 0 .
v.c:9: note: vect_model_simple_cost: inside_cost = 1, outside_cost = 1 .
v.c:9: note: vect_model_reduction_cost: inside_cost = 1, outside_cost = 6 .
v.c:9: note: cost model: prologue peel iters set to vf/2.
v.c:9: note: cost model: epilogue peel iters set to vf/2 because peeling for alignment is unknown .
v.c:9: note: Cost model analysis:
  Vector inside of loop cost: 3
  Vector outside of loop cost: 27
  Scalar iteration cost: 3
  Scalar outside cost: 7
  prologue iterations: 2
  epilogue iterations: 2
  Calculated minimum iters for profitability: 8

v.c:9: note:   Profitability threshold = 7

v.c:9: note: Profitability threshold is 7 loop iterations.
v.c:9: note: LOOP VECTORIZED.
v.c:5: note: vectorized 1 loops in function.

Answer 1:

在第一种情况 :代码覆盖相同的存储器位置a[i]中的每个迭代。 这在本质上sequentializes循环的循环迭代不是独立的。
(在现实中,实际上只需要最后的迭代。所以整个内环可以取出来。)

在第二种情况 :GCC识别环路减少操作-为它具有特殊的办案来量化。

编译器矢量常常被实现为某种“模式匹配”的。 这意味着编译器分析代码,看看它是否符合一定的模式,它是能够量化。 如果是的话,它就会被量化。 如果没有,那么它没有。

这似乎是一个角情况下第一循环不适合任何的预编码模式,海湾合作委员会可以处理的。 但第二种情况符合“向量化减排”模式。


下面是吐出的那个GCC的源代码中的相关部分"not vectorized: live stmt not supported: "消息:

http://svn.open64.net/svnroot/open64/trunk/osprey-gcc-4.2.0/gcc/tree-vect-analyze.c

if (STMT_VINFO_LIVE_P (stmt_info))
{
    ok = vectorizable_reduction (stmt, NULL, NULL);

    if (ok)
        need_to_vectorize = true;
    else
        ok = vectorizable_live_operation (stmt, NULL, NULL);

    if (!ok)
    {
        if (vect_print_dump_info (REPORT_UNVECTORIZED_LOOPS))
        {
            fprintf (vect_dump, 
                "not vectorized: live stmt not supported: ");
            print_generic_expr (vect_dump, stmt, TDF_SLIM);
        }
        return false;
    }
}

从刚才的路线:

vectorizable_reduction (stmt, NULL, NULL);

很显然,GCC的检查,看它是否符合一个“矢量化减少”的格局。



Answer 2:

GCC矢量化可能不是足够聪明的矢量化的第一环。 加入的情况下更容易向量化,因为a + 0 == a 。 考虑SIZE==4

  0 1 2 3 i
0 X
1 X X
2 X X X
3 X X X X
j

X表示的组合ija将被分配给或增加。 对于添加的情况下,我们可以计算的结果b[i] > c[j] ? b[i] : c[j] b[i] > c[j] ? b[i] : c[j] ,比如说, j==1i==0..4 ,放入矢量D 。 然后,我们只需要零点D[2..3]并添加所得的载体以a[0..3] 对于转让的情况下,它更是一个有点棘手。 我们不仅要零D[2..3]而且零A[0..1]然后才相结合的结果。 我想这是在向量化失败。



Answer 3:

第一环路相当于

#define SIZE (65536)

int a[SIZE], b[SIZE], c[SIZE];

int foo () {
  int i, j;

  for (i=0; i<SIZE; i++){
    a[i] = b[i] > c[SIZE - 1] ? b[i] : c[SIZE - 1];
  }
  return a[0];
}

与原表达的问题是,它真的没有那么多感觉,所以这不是太奇怪GCC无法向量化它。



Answer 4:

第一个只是平凡的改变[]很多次(临时)。 第二个使用每次[](不临时)的最后值。

直到版本补丁,你可以使用“挥发性”变量来量化。

使用

int * c=malloc(sizeof(int));

使其一致;

v.c:9: note: Unknown alignment for access: c

示出了“C”具有比b和a不同的存储区域。

我认为“MOVAPS”般的指令是“矢量”(从SSE-AVX指令表)

这里: http://gcc.gnu.org/projects/tree-ssa/vectorization.html#using

第6和第7的例子显示出类似的困难。



文章来源: GCC: vectorization difference between two similar loops