2

次の単純なコードは、gcc 4.7.0 で期待される結果をもたらしません。これは正しいですか、それともバグですか?

  unsigned count_err(std::vector<unsigned> const&num, unsigned mask)
  {
    unsigned c=0;
    // enables to reuse the lambda later (not in this simple example)
    auto f = [&] (unsigned i) { if(i&mask) ++c; };
#pragma omp parallel for reduction(+:c)
    for(unsigned i=0; i<num.size(); ++i)
      f(num[i]);
    return c;
  }

cこれはゼロを返します:ラムダ関数からの削減は行われません。ところで、結果はシリアル関数によって返されると予想していました

  unsigned count_ser(std::vector<unsigned> const&num, unsigned mask)
  {
    unsigned c=0;
    auto f = [&] (unsigned i) { if(i&mask) ++c; };
    std::for_each(num.begin(),num.end(),f);
    return c;
  }

次の実装では、期待される結果が得られます (どちらの場合も、リダクション変数のインクリメントを行うコード定義は並列領域に移動されます)。

  unsigned count_ok1(std::vector<unsigned> const&num, unsigned mask)
  {
    unsigned c=0;
    auto f = [&] (unsigned i) -> bool { return i&mask; };
#pragma omp parallel for reduction(+:c)
    for(unsigned i=0; i<num.size(); ++i)
      if(f(num[i])) ++c;
    return c;
  }

  unsigned count_ok2(std::vector<unsigned> const&num, unsigned mask)
  {
    unsigned c=0;
#pragma omp parallel reduction(+:c)
    {
      auto f = [&] (unsigned i) { if(i&mask) ++c; };
#pragma omp for
      for(unsigned i=0; i<num.size(); ++i)
        f(num[i]);
    }
    return c;
  }

count_err()異なる結果をもたらすという事実は、コンパイラのバグですか、それとも正しいですか?

4

1 に答える 1

7

コンパイラのバグではないと思います。これが私の説明です。c最初の例では、ラムダがグローバル変数への参照を保持していたと思います。のスレッド ローカル コピーはc、for ループに入ったときに作成されました。そのため、スレッドは同じグローバル変数をインクリメントしていました (同期なし)。ループを終了すると、スレッド ローカル コピーc(ラムダはそれらを認識しないため、すべてゼロに等しい) が合計されて 0 になります。ラムダがローカルコピーcount_ok2への参照を保持しているため、バージョンは機能します。c

于 2012-08-22T10:49:14.690 に答える