3

を反復処理するために、次のコードを指定しましたstd::tuple。コードはこちらから

#include <tuple>
#include <utility>

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
for_each(std::tuple<Tp...> &, FuncT) // Unused arguments are given no names.
{ }

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_each(std::tuple<Tp...>& t, FuncT& f)
{
    f(std::get<I>(t));
    for_each<I + 1, FuncT, Tp...>(t, f);
}

for_eachここで、 openmp on を使用できるのと同じ方法で、このループを openmp で実行したいと思いforます。これを可能にするトリックはありますか?

注: 上記のコードを変更するか、独自の の他のバージョンを使用できますfor_each

4

1 に答える 1

2

C++11 テンプレートの構文は私にとって非常に異質ですが、このような再帰的な問題は、明示的な OpenMP タスクを使用して並列化するのが最適です。

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_each(std::tuple<Tp...>& t, FuncT& f)
{
    #pragma omp task firstprivate(I) shared(t,f)
    {
        f(std::get<I>(t));
    }
    for_each<I + 1, FuncT, Tp...>(t, f);
}

...

// Proper usage
#pragma omp parallel
{
    #pragma omp single
    for_each(...);
}

for_each重要な部分は、領域内のsingle構成でトップレベルの呼び出しを行うことparallelです。したがって、単一のスレッドのみが を呼び出しfor_each、その結果f(std::get<I>(t));、明示的なタスクとして後で実行するためにキューに入れられます。他のスレッドは、コンストラクトの最後にある暗黙のバリアで待機している間single、タスク キューからタスクのプルを開始し、キューが空になるまでそれらを並行して実行します。わかりやすくするために、タスクで使用されるすべての変数の共有クラスが明示的に示されています。

参照するオブジェクトは共有する必要がtありf、参照自体 (基本的には参照を実装するポインター) は firstprivate にする必要があります。一方、OpenMP 標準では、参照型を firstprivate にすることを禁止しており、コンパイラ ベンダーによって標準の実装方法が異なる傾向があります。インテル C++ コンパイラーは次のコードを受け入れ、タスク内で正しい結果を返しますが、参照される変数はプライベート化されます (これは間違っています)。

void f(int& p)
{
   #pragma omp task
   {
      cout << "p = " << p << endl;
      p = 3;
      cout << "p' = " << p << endl;
   }
}

void f1()
{
   int i = 5;

   #pragma omp parallel
   {
      #pragma omp single
      f(i);
   }
   cout << "i = " << i << endl;
}

PGI のコンパイラは正しい結果を提供し、プライベート化は行いませんi。反対に、GCC はそれが必要であると正しく判断しpますfirstprivateが、標準の禁止事項に遭遇し、コンパイル時エラーが発生します。

タスクを読み取るように変更すると、次のようになります。

#pragma omp task shared(p)
{
    ...
}

GCC では正しく動作しますが、タスクは間違った初期値を出力pし、インテル C++ コンパイラーと PGI の C++ コンパイラーの両方でセグメンテーション違反を引き起こします。

図に行く!

于 2013-08-02T17:21:36.457 に答える