c++ - OpenMP 返回错误结果

标签 c++ multithreading performance parallel-processing openmp

下面的代码应该使用 4 个线程来计算 0 到 1000 之间所有数字的总和。它应该返回 499500,但它在每次执行时返回不同的值。

#include <omp.h>
#include <iostream>

using namespace std;

int main (int argc, char *argv[])
{
        int nthreads, i, tid;
        float total;

        #pragma omp parallel num_threads(4) 
        {
            tid = omp_get_thread_num();
            if (tid == 0) {
                nthreads = omp_get_num_threads();
                cout << "Número de threads = " << nthreads <<endl;
            }
           #pragma omp barrier

           total = 0.0;
           #pragma omp for schedule(dynamic,10) private(i)
           for (i=0; i<1000; i++)
               total = total + i*1.0;
        } 
        cout << "Total = " <<total << endl;
        return 0;
}

最佳答案

您的代码中发生的事情是您有多个线程同时修改变量 total 的值。要解决此问题,您可以使用来自 OpenMP standard 的 OpenMP reduction 子句可以阅读:

The reduction clause can be used to perform some forms of recurrence calculations (...) in parallel. For parallel and work-sharing constructs, a private copy of each list item is created, one for each implicit task, as if the private clause had been used. (...) The private copy is then initialized as specified above. At the end of the region for which the reduction clause was specified, the original list item is updated by combining its original value with the final value of each of the private copies, using the combiner of the specified reduction-identifier.

有关reduction 子句如何工作的更详细解释,请查看此 SO Thread .

因此,为了解决代码中的竞争条件,只需将其更改为:

        #pragma omp for schedule(dynamic,10) private(i) reduction(+:total)
        for (i=0; i<1000; i++)
             total = total + i*1.0;

应用归约子句后,为避免错误结果,不要在并行区域内初始化共享变量total:

total = 0.0;

只需将其设置在平行区域之前即可。

关于变量 tid 本身还有另一个竞争条件,它在线程之间共享并在并行区域内同时更新:

tid = omp_get_thread_num();

这可以通过使 tid 对每个线程私有(private)来解决,例如:

int tid = omp_get_thread_num();

边注

在 OpenMP 中,被 #pragma omp for 包围的最外层循环的索引变量(在此上下文中为 i)已经是私有(private)的,因此子句 private (i) 并不是真正必要的。

另一点是schedule(dynamic,10);除非您只是玩弄,否则使用调度 static 实际上更有意义(性能方面),因为代码不会导致负载平衡问题。 dynamic 调度具有在运行时将任务分配给线程的额外开销,而 static 调度中的分配是在编译时执行的-时间。

最后,如下:

    tid = omp_get_thread_num();
    if (tid == 0) {
        nthreads = omp_get_num_threads();
        cout << "Número de threads = " << nthreads <<endl;
    }

可以通过使用 OpenMP master clause 来简化,即:

    #pragma omp master
    {
        nthreads = omp_get_num_threads();
        cout << "Número de threads = " << nthreads <<endl;
    }

应用了更改的正在运行的解决方案:

#include <omp.h>
#include <iostream>

using namespace std;

int main (int argc, char *argv[])
{
        float total = 0.0;

        #pragma omp parallel num_threads(4) 
        {
           #pragma omp master
           {
                int nthreads = omp_get_num_threads();
                cout << "Número de threads = " << nthreads <<endl;
           }
           #pragma omp barrier

           #pragma omp for reduction(+:total)
           for (int i=0; i < 1000; i++)
               total = total + i*1.0;
        } 
        cout << "Total = " <<total << endl;
        return 0;
}

输出:

Número de threads = 4
Total = 499500

关于c++ - OpenMP 返回错误结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65993529/

相关文章:

c++ - 在 universal_time 上使用时间方面

python - Cython C++ 示例失败,为什么?

javascript - 单线程脚本语言中可以有竞争条件吗?

javascript - 如何检查我的 JavaScript 是否正确缓存

php - 我应该限制自己在一个页面上使用多少个 MySQL 查询? PHP/MySQL

c++ - 如何为布局激活 adjustsize()

c++ - 如何在 Windows 下轻松地将一个很长的字符串传递给工作进程?

java - ExecutorService 与任务链关闭

java - 是增加工作线程的数量还是在 Netty 中创建自己的线程池更好?

mysql - 表级锁定保证什么?