#pragma simd
是开发人员用来强制执行矢量化的显式矢量化工具,如
https://software.intel.com/en-us/node/514582所述,而
#pragma vector
是一种工具,用于指示编译器基于其参数矢量化循环。这里的参数是
always
,这意味着“忽略编译器的成本/效率启发式,并继续进行矢量化”。有关
#pragma vector
的更多信息,请访问
https://software.intel.com/en-us/node/514586。这并不意味着
#pragma simd
会产生错误结果,它成功地将循环矢量化,而
#pragma vector always
未能矢量化。当
#pragma simd
与正确的从句集一起使用时,它可以进行矢量化并仍然产生正确的结果。
下面是一个小的代码片段,演示了这一点:
void foo(float *a, float *b, float *c, int N) {
#pragma vector always
#pragma ivdep
//#pragma simd vectorlength(2)
for(int i = 2; i < N; i++)
a[i] = a[i-2] + b[i] + c[i];
return;
}
使用ICC编译此代码将生成以下矢量化报告:
$ icc -c -vec-report2 test11.cc
test11.cc(5): (col. 1) remark: loop was not vectorized: existence of vector dependence
默认情况下,ICC使用128位的XMM寄存器进行SSE2操作。每个XMM寄存器可以容纳4个浮点数,但是当您尝试容纳一个四个浮点数向量时,会出现向量依赖性。所以#pragma vector总是正确的。但是,如果我们只考虑两个浮点数,就可以将此循环矢量化而不会破坏结果。相同的矢量化报告如下所示:
void foo(float *a, float *b, float *c, int N){
#pragma simd vectorlength(2)
for(int i = 2; i < N; i++)
a[i] = a[i-2] + b[i] + c[i];
return;
}
$ icc -c -vec-report2 test11.cc
test11.cc(5): (col. 1) remark: SIMD LOOP WAS VECTORIZED
但是
#pragma vector
没有一个子句可以明确指定在向量化循环时要考虑的向量长度。这就是
#pragma simd
真正派上用场的地方。
当使用正确的子句最好地解释了向量计算时,编译器将生成所请求的向量,不会生成错误的结果。Intel(R) Cilk(TM) Plus白皮书发布在
https://software.intel.com/sites/default/files/article/402486/intel-cilk-plus-white-paper.pdf中有一节"Usage of $pragma simd vectorlength clause"和"Usage of $pragma simd reduction and private clause",其中讲解了如何使用带有正确子句的pragma simd子句。这些子句帮助开发人员向编译器表达他想要实现的目标,编译器据此生成向量代码。强烈建议在需要最好表达循环逻辑给编译器的地方使用#pragma simd与相关子句。
此外,传统上内部循环是向量化的目标,但pragma simd也可用于向量化外部循环。有关更多信息,请访问
https://software.intel.com/en-us/articles/outer-loop-vectorization。