【问题标题】:Intel and GNU C compilers contradict themselves w.r.t vectorisation英特尔和 GNU C 编译器在向量化方面自相矛盾
【发布时间】:2013-04-30 08:57:29
【问题描述】:

在课堂上,我们得到了一个应该向量化的简单循环。这很顺利,但我们遇到了一件奇怪的事情。考虑这段代码:

#include<stdio.h>

void func(int N, double *a, double *b, double *c, double *d) {
  int i;
  #pragma ivdep
  for ( i=0; i<N; i++ ) {
    d[i] = c[i+1];
  }
  #pragma ivdep
  for ( i=0; i<N; i++ ) {
    a[i] = b[i];
    c[i] = a[i] + b[i];
  }
}

这是 ICC 的输出(命令icc -O2 -vec-report3 -c example.c,版本 13.0.1):

example.c(6): (col. 3) remark: LOOP WAS VECTORIZED.
example.c(6): (col. 3) remark: loop was not vectorized: not inner loop.
example.c(10): (col. 3) remark: LOOP WAS VECTORIZED.

我的汇编程序不够流利,无法阅读-S 转储,所以我不知道它实际上做了什么;但由于没有理由我可以理解 not 对第一个循环进行矢量化,所以我认为可以。

这些相互矛盾的信息的原因是什么?

在开放方面,GCC 4.5.4(命令gcc -O3 -ftree-vectorizer-verbose=1 -c example.c)对两个循环进行矢量化。另一方面,GCC 4.6.4 打印:

example.c:10: note: created 3 versioning for alias checks.
example.c:10: note: LOOP VECTORIZED.
example.c:3: note: vectorized 1 loops in function.

GCC 4.8.0 更加冗长:

Analyzing loop at example.c:10
Vectorizing loop at example.c:10
example.c:10: note: create runtime check for data references *_24 and *_21
example.c:10: note: create runtime check for data references *_24 and *_27
example.c:10: note: create runtime check for data references *_21 and *_27
example.c:10: note: created 3 versioning for alias checks.
example.c:10: note: === vect_do_peeling_for_loop_bound ===Setting upper bound of nb iterations for epilogue loop to 0
example.c:10: note: LOOP VECTORIZED.
Analyzing loop at example.c:6
example.c:3: note: vectorized 1 loops in function.
example.c:10: note: Turned loop into non-loop; it never loops.

两者都没有说任何关于第一个循环的注意事项,但 4.8.0 似乎在第二个循环上自相矛盾。

这是怎么回事?

【问题讨论】:

    标签: c gcc vectorization compiler-optimization icc


    【解决方案1】:

    认为发生的情况是第一个循环足够简单,编译器已经有一个矢量化版本,所以它不是生成矢量化代码,而是替换为对已经优化的调用版本(希望已经包含别名检查)。

    至于第二个循环,由于它们可能是指针之间的别名(您可以通过限制指针判断是否没有别名:http://en.wikipedia.org/wiki/Restrict),因此需要进行一些运行时检查以确保没有别名。根据别名,第二个循环的不同矢量化版本可能适用。

    【讨论】:

      【解决方案2】:

      如果您删除 #pragma ivdep 并引入如下所示的限制指针,您会看到 icc 和 gcc 的相同行为:

      #include<stdio.h>
      
      void func(int N, double *__restrict__ a, double *__restrict__ b, double *__restrict__ c, double *__restrict__ d) {
        int i;
        for ( i=0; i<N; i++ ) {
          d[i] = c[i+1];
        }
        for ( i=0; i<N; i++ ) {
          a[i] = b[i];
          c[i] = a[i] + b[i];
        }
      }
      

      来自 icc 的矢量化报告是:

      $ icc -c test.cc -vec-report2
      test.cc(5): (col. 3) remark: LOOP WAS VECTORIZED
      test.cc(5): (col. 3) remark: loop was not vectorized: not inner loop
      test.cc(8): (col. 3) remark: LOOP WAS VECTORIZED
      

      来自 gcc 的矢量化报告是:

      $ gcc -c -O3 -ftree-vectorizer-verbose=1 test.cc

      test.cc:8: note: LOOP VECTORIZED.
      test.cc:5: note: LOOP VECTORIZED.
      test.cc:3: note: vectorized 2 loops in function.
      

      这里使用的 gcc 版本是 4.4.4。通过指定restrict关键字,我们的内存别名检查被icc和gcc都忽略了。

      【讨论】:

      • 也许我遗漏了什么,但这如何回答我的问题?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-03
      • 1970-01-01
      • 2021-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-14
      相关资源
      最近更新 更多