我正在使用Intel Cilk Plus数组表示法来练习矢量编程。然而,我遇到了数组赋值的奇怪复制行为。
要解决的问题是并行前缀。D是输入,P是输出。
//错误的结果代码。在英特尔icc 14.0.2上测试。
void vec_prefix(int n, int D[n], int P[n]) {
P[0:n] = D[0:n]; //initial copy
int bound=1;
int * Buf1 = _mm_malloc(n*sizeof(int), 16);
int * Buf2 = _mm_malloc(n*sizeof(int), 16);
while(bound<n){
Buf1[0:n-bound] = P[0:n-bound];
Buf2[0:n-bound] = P[bound:n-bound];
//printf("WHY??n"); //Add this fence, the result will be correct.
P[bound:n-bound] = Buf1[0:n-bound] + Buf2[0:n-bound];
bound<<=1;
}
_mm_free(Buf1); _mm_free(Buf2);
}
如果我删除行"printf"的注释,结果是正确的。否则,就错了。但是,如果所有副本都遵循英特尔文档中的描述,则代码应该是正确的。
似乎如果没有内存围栏,前两行的Buf1/Buf2复制就没有完成,后面的加法操作使用了一些不稳定的值。或者,编译器优化器只使用复制传播来删除副本,并创建"P[绑定:n-bound]=P[0:n-bound]+P[绑定:n-绑定]"。英特尔文档中未对此进行定义。
//正确的结果代码
void vec_prefix(int n, int D[n], int P[n]) {
P[0:n] = D[0:n]; //initial copy
int bound=1;
int * Buf1 = _mm_malloc(n*sizeof(int), 16);
while(bound<n){
//direct copy part
Buf1[0:bound] = P[0:bound];
//add part
Buf1[bound:n-bound] = P[bound:n-bound] + P[0:n-bound];
//copy back
P[0:n] = Buf1[0:n];
bound<<=1;
}
_mm_free(Buf1);
}
参考:英特尔文档
- http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011Update/compiler_c/optaps/common/optaps_par_cean_prog.htm
- https://www.cilkplus.org/tutorial-array-notation
您的代码在我看来是正确的。不需要"围栏"。我向英特尔编译器组提交了一份内部错误报告,并以所有错误的示例作为输入。谢谢你的例子。
如果编译器工作正常,您甚至可以将while循环缩短为:
while(bound<n){
Buf1[0:n-bound] = P[0:n-bound];
P[bound:n-bound] = Buf1[0:n-bound] + P[bound:n-bound];
bound<<=1;
}
第二个数组段分配是可以的,因为p的重叠正好是。遗憾的是,icc在这个例子中也展示了它的缺陷。