OpenMP - 并行和嵌套循环

时间:2018-05-25 09:55:27

标签: c++ parallel-processing openmp

目前,我正在尝试实现拉普拉斯方程的算法。我已经看了几个实现,并且我坚持认为放置OpenMP的pragma声明的最佳位置。

while( var > tol && iter <= maxIter ) {
    ++iter;
    var = 0.0;

    for (i=1; i<=n; ++i)
        for (j=1; j<=n; ++j) {         
            Tnew[i*n2+j] = 0.25*( T[(i-1)*n2+j] + T[(i+1)*n2+j] 
                                + T[i*n2+(j-1)] + T[i*n2+(j+1)] );

            var = fmax(var, fabs(Tnew[i*n2+j] - T[i*n2+j]));
        }
}

就个人而言,我认为最好的情况是将它置于内循环之前,因为我认为它不会导致依赖性问题。但是,一位朋友告诉我,这样做太贵了。什么是最好的方法,为什么?

1 个答案:

答案 0 :(得分:0)

首先,我认为您忘记在所有外循环迭代结束时交换数组TnewT。 假设这些是指针,这里是经过纠正的串行代码:

while(var > tol && iter <= maxIter) {
    ++iter;
    var = 0.0;
    for (i=1; i<=n; ++i)
        for (j=1; j<=n; ++j) {         
            Tnew[i*n2+j] = 0.25*(T[(i-1)*n2+j] + T[(i+1)*n2+j] 
                                + T[i*n2+(j-1)] + T[i*n2+(j+1)]);
            var = fmax(var, fabs(Tnew[i*n2+j] - T[i*n2+j]));
        }
    }
    temp = TNew;
    TNew = T;
    T = temp;
}

现在,您可能希望尝试并行化i - 循环。请注意,var必须是共享变量,因此会在最里面的并行区域中对其更新进行竞争;我们将使用critical构造来确保原子性。这是näive代码:

while(var > tol && iter <= maxIter) {
    ++iter;
    var = 0.0;
    #pragma omp parallel for
    for (i=1; i<=n; ++i)
        for (j=1; j<=n; ++j) {         
            Tnew[i*n2+j] = 0.25*(T[(i-1)*n2+j] + T[(i+1)*n2+j] 
                                + T[i*n2+(j-1)] + T[i*n2+(j+1)]);
            #pragma omp critical
            {
                var = fmax(var, fabs(Tnew[i*n2+j] - T[i*n2+j]));
            }
        }
    }
    temp = TNew;
    TNew = T;
    T = temp;
}

正如你的朋友正确指出的那样,如果parallel区域被创建并被大量销毁,则线程创建/终止的开销可能很高。因此,在mastersingle构造的帮助下,我们现在应该尝试将此并行构造移动到最外层循环之外。以下是示例翻译:

#pragma omp parallel private(iter) shared(var, maxIter)
{   
    while(var > tol && iter <= maxIter) {
        ++iter;
        #pragma omp barrier
        #pragma omp single
        {
            var = 0.0;
        }
        #pragma omp for
        for (i=1; i<=n; ++i)
            for (j=1; j<=n; ++j) {         
                Tnew[i*n2+j] = 0.25*(T[(i-1)*n2+j] + T[(i+1)*n2+j] 
                                    + T[i*n2+(j-1)] + T[i*n2+(j+1)]);
                #pragma omp critical
                {
                    var = fmax(var, fabs(Tnew[i*n2+j] - T[i*n2+j]));
                }
            }
        }
        #pragma omp single
        {
            temp = TNew;
            TNew = T;
            T = temp;
        }
    }
}

请注意,私有化iter非常重要。另请注意,显式和隐式障碍确保了对varTNewT的不同访问之间没有竞争。

关于critical构造:请注意,在最内层循环内多次调用关键区域可能成本很高。您应该使用私有变量来获取每个线程的最大差异,然后使用for循环的外部来获取var的正确值。我会把实际的翻译留给你。

建议:测试一些大数据集的代码,即n(约500-2000)的一些大值和tol的小值(约0.01-0.001),与串行代码相比,可以看到任何可见的加速;否则,同步成本可能会中和任何并行性增益,使并行版本的效率低于串行版本。