C OpenMP矩阵乘法临界段

C OpenMP矩阵乘法临界段,c,parallel-processing,openmp,matrix-multiplication,C,Parallel Processing,Openmp,Matrix Multiplication,我正在尝试并行化矩阵乘法的最内层循环。但是,每当有多个线程时,矩阵乘法就不会在输出数组中存储正确的值,我正在试图找出原因 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

我正在尝试并行化矩阵乘法的最内层循环。但是,每当有多个线程时,矩阵乘法就不会在输出数组中存储正确的值,我正在试图找出原因

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矩阵(){
int i,j,k,和;
对于(i=0;i
我还尝试使用:

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;
    }
}
}
void矩阵(){
int i,j,k,和;
对于(i=0;i
但这也不起作用。我还尝试了不使用第二个#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;
    }
}
}
void matrixC(){
int i,j,k,sum,np;
对于(i=0;i
我是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;
}
双A[N][N];
双B[N][N];
双C[N][N];
int=CH;
空矩阵序列(){
int i,j,k,和;
对于(i=0;i
求和
作为一个归约变量是正确的方法,应该有效(请参阅)。请注意,您仍然必须声明共享和私有变量,例如
k

更新
在您更新以提供MVCE后,@Zboson发现了实际的错误:您将数组声明为
double
,但将它们添加为
int

IEEE浮点算法是不关联的,即
(a+b)+c
不一定等于
a+(b+c)
。因此,减少数组的顺序很重要。当您在不同的线程之间分配数组元素时,它会更改顺序和的顺序。使用SIMD也会发生同样的情况。例如,请参见使用SIMD进行重复的这一极好的问题:

除非您告诉编译器,否则编译器通常不会使用关联浮点运算。e、 g.使用GCC的
-Ofast
,或
-ffast math
,或
-fassocialive math
。比如说

但是,当您使用OpenMP时,它会自动假定关联数学至少用于分配块(在卡盘中,编译器仍然不会使用关联数学,除非您告诉它),从而违反IEEE浮点规则。许多人没有意识到这一点


由于减少取决于顺序,您可能对减少数值不确定性的结果感兴趣。一种解决方案是使用。

感谢您的回复!但遗憾的是,我也尝试过这个。我把它改为:#pragma omp parallel for shared(I,j)private(k)reduction(+:sum),但仍然没有得到正确的和。现在是凌晨3点,否则我会尝试测试这个。示例代码是否在您的系统上正确运行?我不知道有什么区别。我最好的工作有些是在凌晨3点完成的:)但我最差的工作有些也是在凌晨3点完成的,所以我理解。非并行版本的代码可以正常工作。另一个版本,我将最外层的循环并行化,效果很好。只是这个最里面的循环似乎不想合作。你能折叠前两个循环吗?可能没有帮助,因为您引用了i和j。使用的是
N
的哪些值?您是如何分配2D阵列的?您是使用malloc还是像
inta[N][N]
那样声明它们。你能展示一个完整的代码示例吗?顺便说一句,在内部循环上分配线程是并行化矩阵乘法的一种低效方法。@Zboson我已经编辑了我的文章,包含了完整的代码。在测试过程中,我使用了一个很大的N值(超过1000)。我知道效率低下,但我想这仍然是可能的!您的一个问题可能是数组是
double
,但您将
sum
定义为
int
(您可以
int i,j,k,sum;
)。另一个问题是,浮点运算不具有关联性,因此无论如何,使用多个线程都不能得到相同的结果。@Zboson哇,好主意,我不敢相信我一直在过度关注这个问题!它修复了所有问题,甚至我如何并行我的循环都没有问题。非常感谢你,我不知道如果你来我会浪费多少时间