【问题标题】:Strange behaviour of lambda function inside parallel block in openmp c++openmp c ++中并行块内lambda函数的奇怪行为
【发布时间】:2020-05-30 19:47:25
【问题描述】:

我已经开始学习 OpenMP 几天了。我遇到了这个我无法解决的错误。 我定义了一个 lambda 函数 f,它捕获局部变量 s。现在,如果我在并行 for 循环中更改变量 s(每个线程的私有变量),那么函数 f 不会反映其输出的变化,并且总是给出 0。我可以理解我在范围界定方面出现了一些错误,但找不到错误。

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

int main(int argc, char *argv[])
{

    using namespace std;
    double s;
    auto f =[&]{return s;};

#pragma omp parallel for private(s)
        for(int i = 0; i < 4 ; i++)
    {
        s = 5+i;
        double a1 = f();
#pragma omp critical
        cout << a1 << endl;
    }

    return 0;
}

如果我在并行 for 循环中定义 lambda,它实际上可以工作并返回正确的值 s 而不是 0,即

#pragma omp parallel for private(s)
            for(int i = 0; i < 4 ; i++)
        {
            s = 5+i;
            double a1 = [&]{return s;}();
    #pragma omp critical
            cout << a1 << endl;
        }

我的猜测是 lambda 函数和捕获的变量需要在同一范围内。 如果 lambda 函数是在并行块之外定义的,如何解决这个问题?

【问题讨论】:

  • 我不熟悉 OpenMP,但我猜private(s) 的意思是“给每个线程自己的s 副本”。但是f 捕获了“真实的”s,即在循环外声明的那个。它不知道那些私人副本。我预测如果你在声明f 的地方写double&amp; s_ref = s;,并使用s_ref 而不是f(),你将有相同的行为。 lambda 是一条红鲱鱼。
  • 回复:如何解决。很难说不知道你最终要达到什么目标。一种方法是让 lambda 以 s 作为参数,而不是捕获它。然后每个线程将传递自己的本地副本。
  • @IgorTandetnik 我也考虑过,将s 作为参数。这肯定会奏效。
  • private 指令声明数据在每个线程的内存中都有一个单独的副本。换句话说,每个线程都有自己的s 副本。你对s 所做的事情在外部范围内看不到。如果您想共享 s 的值,请删除 private 指令。
  • @Wbuck 我最初在并行 omp for 循环中遇到了这个问题。我希望每个线程都有自己的s 副本。我想我应该更改其中的循环代码

标签: c++ parallel-processing scope openmp


【解决方案1】:

您正在处理多个 s 变量。 parallel region(s) 内的 s 变量和外部作用域中声明的 s 变量。

private 指令声明数据在每个线程的内存中都有一个单独的副本。因此,每个线程都有自己的s 副本,它驻留在与在外部范围double s; 中声明的s 不同的内存位置。

当您更改parallel regions 的值时,您只是更改了s 的线程本地副本。

外部作用域中s 的值(由reflambda 中捕获)不会更改。外部s 位于不同的内存位置。

旁注,您应该初始化外部 s 变量:double s{ 0 };

// This value of x will remain unchanged.
int x = 5;

#pragma omp parallel private( x )
{
    // What happens in the `parallel block` stays in the
    // in the `parallel block`. In other words, incrementing this
    // local value of x will not change the value of x declared in the
    // outer scope. That's because this `parallel block` has its own copy of x.
    x++;      
}


// This value of x will change.
int x = 5;

#pragma omp parallel
{
    // The x variable is now shared between each thread. What you
    // do to x here changes the value of x in the outer scope.
    #pragma omp critical
    {
        x++;         
    }             
}

【讨论】:

    【解决方案2】:

    我最近经常看到人们使用捕获作为“处理”某些数据的一种方式。尽管您可以这样做,但捕获更普遍地用于访问对象的特定实例(有点像绑定)。

    我认为它“更好”(也许更通用),如果您可以将数据作为参数传递,或者如果您想共享数据,您可以通过引用传递:

    所以而不是:

    auto f =[&]{return s;};
    

    你可以这样做:

    auto f =[](double dbl){return dbl;}; // by copy
    auto f =[](double &dbl){return dbl;}; // or by ref
    

    然后使用like:

    s = 5+i;
    double a1 = f(s); // Here pass "s" into the lambda
    

    所以,我并不是说不要使用捕获,有时这很好,但看起来你在这里使用参数会更好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-23
      • 2023-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多