OpenMP Matrix Multiplcation关键部分

时间:2015-09-29 08:06:04

标签: c parallel-processing openmp matrix-multiplication

我试图并行化矩阵乘法的最内层循环。但是,只要有超过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;
}

2 个答案:

答案 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