在不同机器上具有不同输出的并行代码
Parallel code with different output on different machines
当我在两台不同的机器上 运行 下面的代码时,我得到了不同的输出,其中一台的输出是正确的 (sum = sum2) 而另一台则不是。
我也不知道为什么
#include <stdio.h>
#include <math.h>
#include <omp.h>
int main(){
const int NX=1000;
const int NY=1000;
float x[NX+2];
float y[NX+2];
float u[NX+2][NY+2];
float x2; //
float y2;
float sum;
float sum2;
for (int i=0; i<NX+2; i++){
for (int j=0; j<NY+2; j++){
x2 = i;
y2 = j;
u[i][j] = x2+ y2;
sum += u[i][j];
}
}
for (int i=0; i<NX+2; i++){
#pragma omp parallel for
for (int j=0; j<NY+2; j++){
x2 = i;
y2 = j;
u[i][j] = x2+ y2;
}
}
for (int i=0; i<NX+2;i++){
for (int j=0; j<NY+2; j++){
sum2 += u[i][j];
}
}
printf("%f \n", sum);
printf("%f", sum2);
}
您需要初始化
的值
float sum;
float sum2;
否则操作时:
sum += u[i][j];
和
sum2 += u[i][j];
导致 undefined behaviour。这就是为什么您会看到两个不同的结果。
将两个变量都设置为零:
float sum = 0;
float sum2 = 0;
使用(至少)标志 -Wall 编译您的代码。如果您这样做了,您会看到以下警告:
main.c:17:7: warning: 'sum2' may be used uninitialized in this function [-Wmaybe-uninitialized]
17 | float sum2;
| ^~~~
main.c:16:7: warning: 'sum' may be used uninitialized in this function [-Wmaybe-uninitialized]
16 | float sum;
| ^~~
性能方面而不是并行化内部循环:
for (int i=0; i<NX+2; i++){
#pragma omp parallel for
for (int j=0; j<NY+2; j++){
x2 = i;
y2 = j;
u[i][j] = x2+ y2;
}
}
您应该描述使用 OpenMP collapse 选项
并行化两个循环时发生的情况
#pragma omp parallel for collapse(2)
for (int i=0; i<NX+2; i++){
for (int j=0; j<NY+2; j++){
u[i][j] = i + j;
}
}
即使 collapse
子句不是一个意见(例如, 它更慢),在性能方面,并行化外循环仍然会更好比内循环。首先,您避免了创建并行区域 NX+2
次的开销。其次,由于外部循环在列上迭代,而内部循环在行上迭代,因此将第一个循环的迭代划分到线程之间会降低 false-sharing.
的可能性
此外,您还可以将其他两个循环并行化。但是,您需要使用 OpenMP reduction clause 来避免更新 sum 和 sum2 变量期间的竞争条件。
最终代码如下所示:
#include <stdio.h>
#include <math.h>
#include <omp.h>
int main(){
const int NX=1000;
const int NY=1000;
float u[NX+2][NY+2];
float sum = 0;
float sum2 = 0;
#pragma omp parallel for reduction(+:sum)
for (int i=0; i<NX+2; i++){
for (int j=0; j<NY+2; j++){
sum += i+j;
}
}
#pragma omp parallel for
for (int i=0; i<NX+2; i++){
for (int j=0; j<NY+2; j++){
u[i][j] = i+j;
}
}
#pragma omp parallel for reduction(+:sum2)
for (int i=0; i<NX+2;i++){
for (int j=0; j<NY+2; j++){
sum2 += u[i][j];
}
}
printf("%f \n", sum);
printf("%f", sum2);
}
当我在两台不同的机器上 运行 下面的代码时,我得到了不同的输出,其中一台的输出是正确的 (sum = sum2) 而另一台则不是。
我也不知道为什么
#include <stdio.h>
#include <math.h>
#include <omp.h>
int main(){
const int NX=1000;
const int NY=1000;
float x[NX+2];
float y[NX+2];
float u[NX+2][NY+2];
float x2; //
float y2;
float sum;
float sum2;
for (int i=0; i<NX+2; i++){
for (int j=0; j<NY+2; j++){
x2 = i;
y2 = j;
u[i][j] = x2+ y2;
sum += u[i][j];
}
}
for (int i=0; i<NX+2; i++){
#pragma omp parallel for
for (int j=0; j<NY+2; j++){
x2 = i;
y2 = j;
u[i][j] = x2+ y2;
}
}
for (int i=0; i<NX+2;i++){
for (int j=0; j<NY+2; j++){
sum2 += u[i][j];
}
}
printf("%f \n", sum);
printf("%f", sum2);
}
您需要初始化
的值float sum;
float sum2;
否则操作时:
sum += u[i][j];
和
sum2 += u[i][j];
导致 undefined behaviour。这就是为什么您会看到两个不同的结果。
将两个变量都设置为零:
float sum = 0;
float sum2 = 0;
使用(至少)标志 -Wall 编译您的代码。如果您这样做了,您会看到以下警告:
main.c:17:7: warning: 'sum2' may be used uninitialized in this function [-Wmaybe-uninitialized]
17 | float sum2;
| ^~~~
main.c:16:7: warning: 'sum' may be used uninitialized in this function [-Wmaybe-uninitialized]
16 | float sum;
| ^~~
性能方面而不是并行化内部循环:
for (int i=0; i<NX+2; i++){
#pragma omp parallel for
for (int j=0; j<NY+2; j++){
x2 = i;
y2 = j;
u[i][j] = x2+ y2;
}
}
您应该描述使用 OpenMP collapse 选项
并行化两个循环时发生的情况#pragma omp parallel for collapse(2)
for (int i=0; i<NX+2; i++){
for (int j=0; j<NY+2; j++){
u[i][j] = i + j;
}
}
即使 collapse
子句不是一个意见(例如, 它更慢),在性能方面,并行化外循环仍然会更好比内循环。首先,您避免了创建并行区域 NX+2
次的开销。其次,由于外部循环在列上迭代,而内部循环在行上迭代,因此将第一个循环的迭代划分到线程之间会降低 false-sharing.
此外,您还可以将其他两个循环并行化。但是,您需要使用 OpenMP reduction clause 来避免更新 sum 和 sum2 变量期间的竞争条件。
最终代码如下所示:
#include <stdio.h>
#include <math.h>
#include <omp.h>
int main(){
const int NX=1000;
const int NY=1000;
float u[NX+2][NY+2];
float sum = 0;
float sum2 = 0;
#pragma omp parallel for reduction(+:sum)
for (int i=0; i<NX+2; i++){
for (int j=0; j<NY+2; j++){
sum += i+j;
}
}
#pragma omp parallel for
for (int i=0; i<NX+2; i++){
for (int j=0; j<NY+2; j++){
u[i][j] = i+j;
}
}
#pragma omp parallel for reduction(+:sum2)
for (int i=0; i<NX+2;i++){
for (int j=0; j<NY+2; j++){
sum2 += u[i][j];
}
}
printf("%f \n", sum);
printf("%f", sum2);
}