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

时间:2012-01-30 09:08:05

标签: c parallel-processing openmp

我有一个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;
}

非常感谢您的帮助

3 个答案:

答案 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共有的数组。可能你在那里有错误的共享问题。 尝试使用填充分配该数组。