我有一个C代码,它计算两组节点(每个节点三个坐标)之间的距离,尽管我的代码已经足够快了,但我想使用并行计算来进一步提高它。我已经找到了一些关于openMP的信息,我现在正在尝试使用它,但有点奇怪。如果没有omp,代码的cpu时间是20秒,加上两行pragma需要160秒!这怎么会发生?
我把我的代码附加在这里
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: %fn",max);
printf("average distance: %fn",avg/(int)(ncft/jump));
free(d);
return max;
}
非常感谢您的帮助
(下面的答案指的是问题中的初始代码,此后通过应用这些建议进行了改进)
您需要阅读更多关于如何使用OpenMP的信息。该规范可在http://www.openmp.org;还有教程和其他资源的链接。
我将指出代码中的一些问题,并给出如何修复这些问题的建议。
float *d = malloc(3*sizeof(float));
float diff;
d
用作临时变量,因此应在#pragma omp parallel for
中标记为private
(见下文),以避免数据争用。同时,我将只使用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中,这是用#pragma omp for
的reduction
子句完成的。
k++;
}
目前,您在每次迭代时递增k
,从而在迭代之间创建不必要的依赖关系,从而导致并行代码中的数据竞争。根据您的代码,k
只是i/jump
的一个方便的"别名",所以只需在迭代开始时将其赋值,并生成private
。
在外循环和内循环上添加#pragma omp parallel
时,需要使用大量同步。
当使用#pragma omp parallel
时,循环之后有一个屏障,所以所有线程都要等到最后一个线程完成。
在您的情况下,您必须等待内循环和外循环中的所有线程,因此使用同步会带来大量开销。
通常最好只在外环上使用#pragma omp parallel
[假设有足够的工作要做…],以最大限度地减少障碍物的数量。
在代码中,您可以将代码写入所有线程通用的数组dist
。可能你在那里有虚假的共享问题。尝试用填充来分配该数组。