我有一个C代码来计算两组节点之间的距离(每个节点三个坐标),即使我的代码已经足够快,我想用并行计算来增加它。我已经找到了一些关于openMP的信息,我现在正试图使用它,但是有点奇怪。没有omp,代码cpu时间是20s,添加两个pragma行需要160s!怎么会发生?
我将我的代码添加到此处
float computedist(float **vG1, float **vG2, int ncft, int ntri2, int jump, float *dist){
int k = 0, i, j;
float min = 0;
float max = 0;
float avg = 0;
float *d = malloc(3*sizeof(float));
float diff;
#pragma omp parallel
for(i=0;i<ncft;i+=jump){
#pragma omp parallel
for(j=0;j<ntri2;j++){
d[0] = vG1[i][0] - vG2[j][0];
d[1] = vG1[i][1] - vG2[j][1];
d[2] = vG1[i][2] - vG2[j][2];
diff = sqrt(pow(d[0],2) + pow(d[1],2) + pow(d[2],2));
if(j==0)
dist[k] = diff;
else
if(diff<dist[k])
dist[k] = diff;
}
avg += dist[k];
if(dist[k]>max)
max = dist[k];
k++;
}
printf("max distance: %f\n",max);
printf("average distance: %f\n",avg/(int)(ncft/jump));
free(d);
return max;
}
非常感谢您的帮助
答案 0 :(得分:5)
(下面的答案是指问题中的初始代码,从那时起应用这些建议得到了改进)
您需要了解有关如何使用OpenMP的更多信息。该规范可在http://www.openmp.org获得;并且有教程和其他资源的链接。
我将在您的代码中指出一些问题,并提供如何解决这些问题的建议。
float *d = malloc(3*sizeof(float));
float diff;
d
用作临时变量,因此应在private
中标记为#pragma omp parallel for
(见下文)以避免数据争用。同时,我只使用3个独立的浮点数而不是动态分配。 diff
也包含临时值,因此也应为private
。
#pragma omp parallel
for(i=0;i<ncft;i+=jump){
#pragma omp parallel
for(j=0;j<ntri2;j++){
您创建了一个并行区域,其中每个线程执行整个循环(因为该区域不包含任何工作共享构造),并且在其中您创建了一个带有新(!)线程集的嵌套区域,每个线程也执行整个内循环。它为您的程序增加了大量开销和不必要的计算。你需要的是#pragma omp parallel for
,只适用于外循环。
d[0] = vG1[i][0] - vG2[j][0];
d[1] = vG1[i][1] - vG2[j][1];
d[2] = vG1[i][2] - vG2[j][2];
diff = sqrt(pow(d[0],2) + pow(d[1],2) + pow(d[2],2));
与并行性无关,但为什么只调用pow
来计算平方?一个好的旧乘法可能更容易阅读和更快。
if(j==0)
dist[k] = diff;
else
if(diff<dist[k])
dist[k] = diff;
由于操作相同(dist[k]=diff;
),因此可以通过将两个条件与||
(逻辑或)相结合来简化代码。
}
avg += dist[k];
if(dist[k]>max)
max = dist[k];
在这里,您可以计算外部循环中的聚合值。在OpenMP中,这是使用reduction
的{{1}}子句完成的。
#pragma omp for
目前,您在每次迭代时增加 k++;
}
,从而在迭代之间创建不必要的依赖关系,从而导致并行代码中的数据竞争。根据您的代码,k
只是k
的一个方便的“别名” - 所以只需在迭代开始时将其分配,并生成i/jump
。
答案 1 :(得分:2)
在外循环和内循环上添加#pragma omp parallel
时,会使用大量同步。
使用#pragma omp parallel
时,循环后有barrier,因此所有线程都会等到最后一个线程完成。
在你的情况下,你必须等待内部循环和外部循环中的所有线程,因此使用syncrhonization会产生大量开销。
通常最好只在外循环上使用#pragma omp parallel
[假设有足够的工作要做...]以尽量减少障碍的数量。
答案 2 :(得分:0)
在您的代码中,您将写入所有线程dist
共有的数组。可能你在那里有错误的共享问题。
尝试使用填充分配该数组。