首页 > 解决方案 > OpenMP For-Loops 在有和没有多线程的情况下产生不同的结果

问题描述

我是多线程的新手,在尝试并行化一些 for 循环时发现了以下问题,我在其中操作 3D 数组。当我只使用一个线程运行代码时,我得到了E_total我期望的值。但是,当我对多线程和 OpenMP使用相同的代码时,我#pragma omp parallel for按以下方式设置

    // DO FIRST COMPUTATION STEP ON 3D ARRAY
    #pragma omp parallel for
    for (size_t ix = 0; ix < N; ix++) {
        for (size_t iy = 0; iy < N; iy++) {
            for (size_t iz = 0; iz < N; iz++) {
                A1[ix][iy][iz] = ...;
            }
        }
    }

    // DO SECOND COMPUTATION --AFTER-- FIRST COMPUTATION
    #pragma omp parallel for
    for (size_t ix = 0; ix < N; ix++) {
        for (size_t iy = 0; iy < N; iy++) {
            for (size_t iz = 0; iz < N; iz++) {
                A2[ix][iy][iz] = ...;
                E_pot += something * A1[ix][iy][iz];
                E_int += something * A2[ix][iy][iz];
            }
        }
    }
    E_total += (E_pot + E_int);    // This result changes when 'omp parallel for' is used

我看到我得到了不同的结果 E_total。由于循环操作是附加的或特定于网格点的(独立于不同的ijk),因此它们不应依赖于循环内的任何排序。

是否有可能在所有先前的第一个循环操作完成之前启动第二个 for 循环?如果是这样,我该如何防止这种情况发生,或者我需要注意哪些其他错误?

抱歉,如果这是一个非常基本的问题,但我在网上找不到相关问题。提前致谢!

标签: c++multithreadingloopsparallel-processingopenmp

解决方案


这段代码的问题是不同线程之间存在竞争条件。和变量在工作线程之间共享E_potE_int因此线程不时地破坏彼此的值。

要解决此问题,请应用该reduction子句(请参阅OpenMP API 规范中的Reduction Clauses and Directives ):

// DO SECOND COMPUTATION --AFTER-- FIRST COMPUTATION
#pragma omp parallel for reduction(+:E_pot) reduction(+:E_int)
for (size_t ix = 0; ix < N; ix++) {
    for (size_t iy = 0; iy < N; iy++) {
        for (size_t iz = 0; iz < N; iz++) {
            A2[ix][iy][iz] = ...;
            E_pot += something * A1[ix][iy][iz];
            E_int += something * A2[ix][iy][iz];
        }
    }
}

您可以查看更多更改,看看它们是否有帮助:

根据它的值,N可能值得在指令中添加一个collapse(2)子句(请参阅Worksharing-Loop Construct)以parallel for将两个外部循环合并为一个循环,然后运行该循环以进行N*N迭代。对于 small N,线程可以更好地工作,因为更多的迭代可以分布在工作线程中。

如果您schedule(static)明确添加(当您什么都不说时,这是大多数 OpenMP 实现的默认设置,但技术上并不能保证),那么您可以添加nowait到第一个循环。这样一来,在第一个并行循环结束时就没有隐含的障碍,并且已经完成其工作块的线程可以继续进行第二个循环。这schedule(static)是需要的,因为第一个和第二个循环都具有相同的并行化,然后这个技巧就起作用了。注意:如果你collapse(2)为第一个循环添加了,那么第二个循环也需要有,collapse(2)这样并行化是一样的。


推荐阅读