我正在编写一个使用OpenMP进行矩阵乘法的程序,为了方便缓存,实现乘法A x B(转置)行X行而不是经典的A x B行x列,以获得更好的缓存效率。这样做我遇到了一个有趣的事实,对我而言是不合逻辑的:如果在这段代码中我并行化extern循环,程序比我在最内层循环中放置OpenMP指令要慢,在我的计算机中,时间是10.9对8.1秒。
//A and B are double* allocated with malloc, Nu is the lenght of the matrixes
//which are square
//#pragma omp parallel for
for (i=0; i<Nu; i++){
for (j=0; j<Nu; j++){
*(C+(i*Nu+j)) = 0.;
#pragma omp parallel for
for(k=0;k<Nu ;k++){
*(C+(i*Nu+j))+=*(A+(i*Nu+k)) * *(B+(j*Nu+k));//C(i,j)=sum(over k) A(i,k)*B(k,j)
}
}
}
答案 0 :(得分:4)
当您并行化外部循环并且编译器无法弄清楚并添加其他锁时,您可能在数据中有一些依赖项。
最有可能它决定不同的外循环迭代可以写入相同的(C+(i*Nu+j))
,并添加访问锁以保护它。
如果您要并行化第二个循环,编译器可能会发现没有依赖关系。但是,弄清楚没有依赖于并行化外部循环对于编译器来说并不是那么简单。
<强>更新强>
一些性能测量。
你好。它看起来像1000双*
和+
不足以支付线程同步的成本。
我做了一些小测试,简单的向量标量乘法对openmp无效,除非元素的数量小于~10'000。基本上,阵列越大,使用openmp就会获得更高的性能。
因此,并行化最内部循环,您必须在不同线程之间分离任务,并将数据收集回1000'000次。
PS。尝试使用英特尔ICC,它可以免费用于学生和开源项目。我记得使用openmp来填充更小的10'000元素阵列。
更新2:缩减示例
double sum = 0.0;
int k=0;
double *al = A+i*Nu;
double *bl = A+j*Nu;
#pragma omp parallel for shared(al, bl) reduction(+:sum)
for(k=0;k<Nu ;k++){
sum +=al[k] * bl[k]; //C(i,j)=sum(over k) A(i,k)*B(k,j)
}
C[i*Nu+j] = sum;
答案 1 :(得分:4)
尝试不经常点击结果。这会导致高速缓存行共享并阻止操作并行运行。使用局部变量将允许大多数写入发生在每个核心的L1缓存中。
此外,使用restrict
可能有所帮助。否则,编译器无法保证对C
的写入不会更改A
和B
。
尝试:
for (i=0; i<Nu; i++){
const double* const Arow = A + i*Nu;
double* const Crow = C + i*Nu;
#pragma omp parallel for
for (j=0; j<Nu; j++){
const double* const Bcol = B + j*Nu;
double sum = 0.0;
for(k=0;k<Nu ;k++){
sum += Arow[k] * Bcol[k]; //C(i,j)=sum(over k) A(i,k)*B(k,j)
}
Crow[j] = sum;
}
}
此外,我认为如果你并行化最内层的循环,Elalfer对于需要减少是正确的。