在下面的C代码中,我在嵌套循环中使用OpenMP。由于竞争条件发生,我想在最后执行原子操作:
double mysumallatomic() {
double S2 = 0.;
#pragma omp parallel for shared(S2)
for(int a=0; a<128; a++){
for(int b=0; b<128;b++){
double myterm = (double)a*b;
#pragma omp atomic
S2 += myterm;
}
}
return S2;
}
问题是#pragma omp atomic
对程序行为没有影响,即使我删除它,也没有任何反应。即使我将其更改为#pragma oh_my_god
,我也没有错误!
我想知道这里出了什么问题,我是否可以告诉编译器在检查omp pragma时更严格,或者为什么我在进行最后一次更改时没有收到错误
PS:对于编译,我使用:
gcc-4.2 -fopenmp main.c functions.c -o main_elec_gcc.exe
PS2:新代码给了我同样的问题并基于gillespie的想法:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <omp.h>
#include <math.h>
#define NRACK 64
#define NSTARS 1024
double mysumallatomic_serial(float rocks[NRACK][3], float moon[NSTARS][3],
float qr[NRACK],float ql[NSTARS]) {
int j,i;
float temp_div=0.,temp_sqrt=0.;
float difx,dify,difz;
float mod2x, mod2y, mod2z;
double S2 = 0.;
for(j=0; j<NRACK; j++){
for(i=0; i<NSTARS;i++){
difx=rocks[j][0]-moon[i][0];
dify=rocks[j][1]-moon[i][1];
difz=rocks[j][2]-moon[i][2];
mod2x=difx*difx;
mod2y=dify*dify;
mod2z=difz*difz;
temp_sqrt=sqrt(mod2x+mod2y+mod2z);
temp_div=1/temp_sqrt;
S2 += ql[i]*temp_div*qr[j];
}
}
return S2;
}
double mysumallatomic(float rocks[NRACK][3], float moon[NSTARS][3],
float qr[NRACK],float ql[NSTARS]) {
float temp_div=0.,temp_sqrt=0.;
float difx,dify,difz;
float mod2x, mod2y, mod2z;
double S2 = 0.;
#pragma omp parallel for shared(S2)
for(int j=0; j<NRACK; j++){
for(int i=0; i<NSTARS;i++){
difx=rocks[j][0]-moon[i][0];
dify=rocks[j][1]-moon[i][1];
difz=rocks[j][2]-moon[i][2];
mod2x=difx*difx;
mod2y=dify*dify;
mod2z=difz*difz;
temp_sqrt=sqrt(mod2x+mod2y+mod2z);
temp_div=1/temp_sqrt;
float myterm=ql[i]*temp_div*qr[j];
#pragma omp atomic
S2 += myterm;
}
}
return S2;
}
int main(int argc, char *argv[]) {
float rocks[NRACK][3], moon[NSTARS][3];
float qr[NRACK], ql[NSTARS];
int i,j;
for(j=0;j<NRACK;j++){
rocks[j][0]=j;
rocks[j][1]=j+1;
rocks[j][2]=j+2;
qr[j] = j*1e-4+1e-3;
//qr[j] = 1;
}
for(i=0;i<NSTARS;i++){
moon[i][0]=12000+i;
moon[i][1]=12000+i+1;
moon[i][2]=12000+i+2;
ql[i] = i*1e-3 +1e-2 ;
//ql[i] = 1 ;
}
printf(" serial: %f\n", mysumallatomic_serial(rocks,moon,qr,ql));
printf(" openmp: %f\n", mysumallatomic(rocks,moon,qr,ql));
return(0);
}
答案 0 :(得分:3)
使用标记-Wall
会突出显示编译指示错误。例如,当我拼错atomic
时,我收到以下警告。
main.c:15: warning: ignoring #pragma omp atomic1
我相信你知道,但为了以防万一,你的例子应该用reduction
使用omp parallel时,默认为共享所有变量。这不是你想要的。例如,每个线程将具有不同的值difx
。相反,你的循环应该是:
#pragma omp parallel for default(none),\
private(difx, dify, difz, mod2x, mod2y, mod2z, temp_sqrt, temp_div, i, j),\
shared(rocks, moon, ql, qr), reduction(+:S2)
for(j=0; j<NRACK; j++){
for(i=0; i<NSTARS;i++){
difx=rocks[j][0]-moon[i][0];
dify=rocks[j][1]-moon[i][1];
difz=rocks[j][2]-moon[i][2];
mod2x=difx*difx;
mod2y=dify*dify;
mod2z=difz*difz;
temp_sqrt=sqrt(mod2x+mod2y+mod2z);
temp_div=1/temp_sqrt;
S2 += ql[i]*temp_div*qr[j];
}
}
答案 1 :(得分:1)
我知道这是一个老帖子,但我认为问题是gcc参数的顺序,-fopenmp应该在编译行的末尾。
答案 2 :(得分:0)
首先,根据实施情况,减少可能比使用原子更好。我会尝试两种方式,然后让他们确定一下。
其次,如果你放弃原子,你可能会或可能不会看到与种族相关的问题(错误的结果)。这完全取决于时间,从一次运行到下一次运行可能会有很大不同。我已经看到过这样的情况,结果在150,000次运行中只出现一次错误,而其他情况则一直出错。
第三,pragma背后的想法是,如果用户没有效果,用户不需要知道它们。除此之外,Unix(及其派生词)的哲学是除非出现问题,否则它是安静的。说,许多实现都有某种标志,因此用户可以获得更多信息,因为他们不知道发生了什么。你可以尝试使用gcc -Wall,至少它应该将oh_my_god pragma标记为被忽略。
答案 3 :(得分:0)
你有
#pragma omp parallel for shared(S2)
for(int a=0; a<128; a++){
....
因此唯一的并行化将是for循环。
如果你想拥有原子或还原 你必须这样做
#pragma omp parallel
{
#pragma omp for shared(S2)
for(int a=0; a<128; a++){
for(int b=0; b<128;b++){
double myterm = (double)a*b;
#pragma omp atomic
S2 += myterm;
} // end of second for
} // end of 1st for
} // end of parallel code
return S2;
} // end of function
否则#之后的所有内容都将成为评论