用于距离计算的并行C代码



我有一个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 forreduction子句完成的。

        k++;
    }

目前,您在每次迭代时递增k,从而在迭代之间创建不必要的依赖关系,从而导致并行代码中的数据竞争。根据您的代码,k只是i/jump的一个方便的"别名",所以只需在迭代开始时将其赋值,并生成private

在外循环和内循环上添加#pragma omp parallel时,需要使用大量同步。

当使用#pragma omp parallel时,循环之后有一个屏障,所以所有线程都要等到最后一个线程完成。
在您的情况下,您必须等待内循环和外循环中的所有线程,因此使用同步会带来大量开销。

通常最好只在外环上使用#pragma omp parallel[假设有足够的工作要做…],以最大限度地减少障碍物的数量。

在代码中,您可以将代码写入所有线程通用的数组dist。可能你在那里有虚假的共享问题。尝试用填充来分配该数组。

最新更新