【问题标题】:OpenMP returning wrong resultOpenMP 返回错误结果
【发布时间】:2021-05-05 16:16:46
【问题描述】:

下面的代码应该使用 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;
}

【问题讨论】:

    标签: c++ multithreading performance parallel-processing openmp


    【解决方案1】:

    您的代码中发生的情况是,您有多个线程同时修改变量total 的值。为了解决这个问题,你可以使用 OpenMP 的reduction 子句,从OpenMP standard 可以读到:

    归约子句可用于执行某些形式的递归 并行计算 (...)。 用于并行和工作共享 构造,创建每个列表项的私有副本,每个列表项一个 隐式任务,好像使用了 private 子句。 (...) 这 然后按上面指定的方式初始化私有副本。在结束时 指定缩减子句的区域,原始列表 通过将其原始值与最终值组合来更新项目 每个私有副本,使用指定的组合器 减少标识符。

    有关 reduction 子句如何工作的更详细说明,请查看SO Thread

    所以要解决代码中的 race-condition,只需将其更改为:

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

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

    total = 0.0;
    

    只需将它设置在平行区域之前。

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

    tid = omp_get_thread_num();
    

    这可以通过将tid 设置为每个线程私有来解决,例如:

    int tid = omp_get_thread_num();
    

    旁注

    在 OpenMP 中,由 #pragma omp for 包围的最外层循环的索引变量(在此上下文中为 i)已经是私有的,因此子句 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
    

    【讨论】:

      猜你喜欢
      • 2020-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多