我试图并行化矩阵乘法的最内层循环。但是,只要有超过1个线程,矩阵乘法就不会在输出数组中存储正确的值,我试图找出原因。
void matrix() {
int i,j,k,sum;
for (i = 0; i < N; i++) {
for (j = 0; j < N; j++){
sum = 0;
#pragma omp parallel for shared(sum,i,j) private(k)
for (k = 0; k < N; k++) {
#pragma omp critical
sum = sum + A[i][k] * B[k][j];
}
C[i][j] = sum;
}
}
}
我也尝试过使用:
void matrix() {
int i,j,k,sum;
for (i = 0; i < N; i++) {
for (j = 0; j < N; j++){
sum = 0;
#pragma omp parallel for shared(sum,i,j) private(k)
for (k = 0; k < N; k++) {
#pragma omp atomic
sum += A[i][k] * B[k][j];
}
C[i][j] = sum;
}
}
}
但那也没有用。我也尝试了没有第二个#pragma,并使用:
void matrixC() {
int i,j,k,sum,np;
for (i = 0; i < N; i++) {
for (j = 0; j < N; j++){
sum = 0;
#pragma omp parallel for reduction(+:sum)
for (k = 0; k < N; k++) {
sum = sum + A[i][k] * B[k][j];
}
C[i][j] = sum;
}
}
}
我是OpenMP的新手,但是从我在线阅读的所有内容来看,这些解决方案中至少有一个应该可行。我知道它可能是竞争条件的问题,同时增加总和,但我不知道为什么它仍然得到错误的总和。
编辑:这是一个更完整的代码版本:
double A[N][N];
double B[N][N];
double C[N][N];
int CHOOSE = CH;
void matrixSequential() {
int i,j,k,sum;
for (i = 0; i < N; i++) {
for (j = 0; j < N; j++) {
sum = 0;
for (k = 0; k < N; k++) {
sum += A[i][k] * B[k][j];
}
C[i][j] = sum;
}
}
}
void matrixParallel() {
int i,j,k,sum;
for (i = 0; i < N; i++) {
for (j = 0; j < N; j++){
sum = 0;
#pragma omp parallel for shared (i,j) private(k) reduction(+:sum)
for (k = 0; k < N; k++) {
sum = sum + A[i][k] * B[k][j];
}
C[i][j] = sum;
}
}
}
int main(int argc, const char * argv[]) {
//populating arrays
int i,j;
for(i=0; i < N; i++){
for(j=0; j < N; j++){
A[i][j] = i+j;
B[i][j] = i+j;
}
}
for(i=0; i < N; i++){
for(j=0; j < N; j++){
C[i][j] = 0;
}
}
if (CHOOSE == 0) {
matrixSequential();
}
else if(CHOOSE == 1) {
matrixParallel();
}
//checking for correctness
double sum;
for(i=0; i < N; i++){
sum += C[i][i];
}
printf("Sum of diagonal elements of array C: %f \n", sum);
return 0;
}
答案 0 :(得分:1)
使sum
缩减变量是正确的方法,并且应该有效(参见https://computing.llnl.gov/tutorials/openMP/#REDUCTION)。请注意,您仍然需要声明共享和私有变量,例如k
。
答案 1 :(得分:0)
IEEE浮点运算不是关联的,即(a+b)+c
不一定等于a+(b+c)
。因此,减少数组的顺序很重要。在不同线程之间分发数组元素时,它会从顺序总和更改顺序。使用SIMD也会发生同样的事情。例如,请参阅使用SIMD执行检索的这个优秀问题:An accumulated computing error in SSE version of algorithm of the sum of squared differences。
除非您告诉它,否则您的编译器通常不会使用关联浮点算法。例如与GCC一起使用-Ofast
,-ffast-math
或-fassocitaive-math
。例如in order to use auto-vectoirization (SIMD) for a reduction the compile requires associtive math。
然而,当你使用OpenMP时,它会自动假设关联数学至少用于分配块(在chucks中,编译器仍然不会使用关联算法,除非你告诉它)违反IEEE浮点规则。很多人都没有意识到这一点。
由于减少取决于您可能对结果感兴趣的顺序,这会降低数值不确定性。一种解决方案是使用Kahan summation。