OpenMP中的还原操作如何在后台运行?

时间:2019-05-29 11:50:25

标签: c openmp hpc reduction

我正在尝试在 parallel-for 循环中优化性能,在该循环中我有一个归约变量(称为 delta ),想知道OpenMP库如何在后台对其进行处理。

让我们以下面的代码为例,在循环的开头,我只是将变量声明为归约变量:

#pragma omp parallel shared(delta, A, B, rows, colms) private(i, j)
.
.
.
    #pragma omp for reduction(+:delta)
            for (i=1; i<=rows; i++){
                for (j=1; j<=colms; j++){
                delta += fabs(A[i][j]- B[i][j]);
                }
    }
.
.
.
//end of parallel region

我想知道在计算期间每个线程在访问 delta 变量时是否设置了锁定,并且是否可以通过将 delta 变量替换为数组< em> delta [number_of_threads] ,其中每个线程在计算过程中将在数组的不同位置写入内容,然后将并行区域之后的所有元素求和。

1 个答案:

答案 0 :(得分:3)

每个线程在其堆栈帧上都有自己的“ delta”副本:

#pragma omp parallel shared(delta, A, B, rows, colms) private(i, j)
{
    double local_delta; // one copy per thread

    __omp_init_schedule(1, rows, &lb, &ub);
    for (i=lb; i<=ub; i++) {
        for (j=1; j<=colms; j++) {
                local_delta += fabs(A[i][j]- B[i][j]);
        }
    }
   __omp_reduce(&delta, local_delta);  // accumulate thread's delta with shared var
   __omp_barrier();                    // do the barrier of the for construct
}

请使用以上代码作为伪代码。实际的代码模式将取决于OpenMP实现可能会执行的实现,内联以及各种其他优化。如果您想阅读一些有关工作原理的信息,请查看[1]和[2]。

__omp_reduce()的实现可以是基于树的,也可以是使用锁或原子指令的顺序实现。 OpenMP实现通常非常聪明,可以为计算机和/或所使用的线程数选择正确的算法。

进行delta[numthreads]修改可能会使性能降低100倍以上,因为这是错误共享的典型示例,因为线程0的delta[0]和线程1的delta[1]在同一缓存行中,这会导致缓存和内存上的大量流量。更好的方法是引入拍拍delta[numthreads * 8](假设delta是8个字节),以便每个线程都有自己的缓存行。但是,您仍然需要执行最后的聚合,并且OpenMP实现可能仍然做得更好。

[1] https://www.dontknow.de/openmp-stuff/the-thing-from-another-world-or-how-do-openmp-compilers-work-part-1/

[2] https://www.dontknow.de/openmp-stuff/thunk-you-very-much-or-how-do-openmp-compilers-work-part-2/