【问题标题】:OpenMP min reduction and std::minOpenMP min 减少和 std::min
【发布时间】:2017-01-16 13:05:41
【问题描述】:

我正在测试 OpenMP 最小缩减。如果我像下面这样编写代码,它将返回正确的结果:res = 3。

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

int main(){

    omp_set_num_threads(5);

    float res=10;

#pragma omp parallel for default(shared) reduction(min:res)
    for(int i1 = 0; i1 <= 10; i1++)
        for(int i0 = 0; i0 <= 10; i0++)
            if(res > 3.0+i1+20*i0)
                res = 3.0+i1+20*i0;

    std::cout << "res = " << res << std::endl;

    return 0;
}

但是如果我用“std::min”替换“if”语句以另一种方式编写,那么结果是错误的:res = 10。

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

int main(){

    omp_set_num_threads(5);

    float res=10;

#pragma omp parallel for default(shared) reduction(min:res)
    for(int i1 = 0; i1 <= 10; i1++)
        for(int i0 = 0; i0 <= 10; i0++)
            res = std::min(res,static_cast<float>(3.0+i1+20*i0));

    std::cout << "res = " << res << std::endl;

    return 0;
}

OpenMP min 减少是否会干扰 std::min?

【问题讨论】:

  • 有趣的问题。我现在无法测试它。 reduction(min: 应该从 OpenMP 3.1 开始工作。另一个有趣的问题是它是否适用于fmin。我想我在手册中看到了一个例子。 fminstd::min 不是一回事,尽管有些人是这么想的。
  • 在阅读了更多标准之后,明确提到min应该像这样实现out=in&lt;out?in:out;也许在你的系统上std:min()不遵守这个......跨度>
  • OpenMP 3.1 手册在第 266 页上给出了一个使用 fmaxf 的示例,所以我认为它也适用于 fminfmax。我虽然 std::min 扩展到三元运算符,所以我不确定它为什么会失败。您将不得不查看宏。
  • here 我解释了为什么 std::min 和 fmin 不一样。
  • std::min 可以通过多种不同的方式实现en.cppreference.com/w/cpp/algorithm/min。您可以做的是使用 OpenMP4.0 创建一个自定义 Reduction 运算符,该运算符将与 std::min 一起使用。

标签: c++ openmp min


【解决方案1】:

首先,您的代码符合要求:您在并行中使用哪种类型的代码并不重要。

reduction 子句的含义是每个线程都有自己的私有副本初始化为min 运算符的中性元素(即缩减项类型中可表示的最大数字),它们将使用它直到构造的结束。此时,这些私有副本将使用缩减标识符(在您的情况下为 min 运算符)缩减为原始列表项。所以这里没有竞争条件。

我使用与您相同的版本执行了您的代码,并且运行良好:icpc (ICC) 16.0.0 和 OpenMP 版本 201307。此问题是否与您使用的 C++ 标准头文件有关?

【讨论】:

    【解决方案2】:

    我会在得出结论之前交换循环。 在类似的情况下,我发现单独的内部和外部循环减少变量是必要的。 -std::min 适用于 icpc 但不适用于 g++ 或 msvc

    【讨论】:

      【解决方案3】:

      问题是你有数据竞争。

      在您的第一个示例中,所有线程中的最小值由 OpenMP 运行时计算:每个线程都有自己的 res,运行时确定最小值。运行时确保res 被所有线程正确读写。

      在您的第二个示例中,每个线程都调用std::min 来确定@​​987654325@ 和另一个值之间的最小值。 resshared 因为你的 default(shared) 子句,所以所有线程都将尝试同时使用和更新 res,这是一个数据竞争。

      您应该继续使用第一个示例。如果你想使用std::min,你必须使用锁或类似的东西来防止数据竞争。

      【讨论】:

      • 谢谢!但是为什么“if”语句中的 res 是私有的,而 std::min 中使用的 res 是共享的?这里(computing.llnl.gov/tutorials/openMP/#REDUCTION)表示只要 res 是归约变量,每个线程都会创建自己的私有副本。
      • 哦,是的,我没有注意到您仍在使用 reduction 子句,这是多余的,std::min 在循环内。在这种情况下,res 对每个变量都是私有的,并且它们的副本永远不会更新/写入在parallel 区域之前声明的res,这就是为什么resparallel 区域之后为 10,即之前分配的值。
      • 但是为什么 res 在第一种情况下会更新呢?在第二种情况下,如果我将 res 初始化为 10000,结果将是 205,对应于 i0=1,i1=2...
      猜你喜欢
      • 1970-01-01
      • 2021-01-12
      • 2011-08-02
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-18
      • 2012-11-05
      相关资源
      最近更新 更多