parallel_for

関数 Foo を配列の各要素に適用させ、各要素を同時に処理しても安全だと仮定します。シーケンシャル・コードは次のようになります。

void SerialApplyFoo( float a[], size_t n ) {
    for( size_t i=0; i!=n; ++i )
        Foo(a[i]);
}

ここでの反復空間は、size_t 型で、範囲は 0 から n-1 までです。テンプレート関数 tbb::parallel_for は、反復空間をチャンクに分け、個別のスレッドで各チャンクを実行します。このループの並列化における最初のステップは、ループボディーをチャンクで動作するような形式に変換することです。この形式は、標準テンプレート・ライブラリー (STL) スタイルのボディー と呼ばれる関数オブジェクトで、operator() がチャンクを処理します。次のコードは、ボディー・オブジェクトを宣言します。インテル® TBB に必要なコードは、太字で示されています。

#include "tbb/tbb.h"
 
using namespace tbb;
 
class ApplyFoo {
    float *const my_a;
public:
    void operator()( const blocked_range<size_t>& r ) const {
        float *a = my_a;
        for( size_t i=r.begin(); i!=r.end(); ++i ) 
           Foo(a[i]);
    }
    ApplyFoo( float a[] ) :
        my_a(a)
    {}
};

サンプルの using 宣言子は、各識別子の前に名前空間のプリフィクス tbb を付けなくてもライブラリー識別子を使用できるようにします。サンプルの残りの部分は、using 宣言子が存在していると仮定しています。

operator() の引数に注目してください。blocked_range<T> は、ライブラリーによって提供されるテンプレート・クラスで、型 T の 1 次元の反復空間を示します。parallel_for クラスは、別の反復空間とも動作します。ライブラリーは、2 次元空間用に blocked_range2d を提供します。「高度なトピック: 異なる種類の反復空間」で説明されているように独自の空間を定義することもできます。

ApplyFoo のインスタンスでは、オリジナルのループの外で定義されるがループ内で使用される、すべてのローカル変数を認識するメンバーフィールドが必要です。通常、ボディー・オブジェクトのコンストラクターはこれらのフィールドを初期化しますが、parallel_for は、ボディー・オブジェクトが作成される方法は問いません。テンプレート関数 parallel_for では、ボディー・オブジェクトに、各ワーカースレッドの個別のコピー (または複数のコピー) を作成するために起動されるコピー・コンストラクターが必要です。また、コピー・コンストラクターはこれらのコピーを破棄するデストラクターも起動します。ほとんどの場合、暗黙的に生成されたコピー・コンストラクターとデストラクターは正常に動作します。正常に動作しない場合は、両方 に一貫性がない場合がほとんどです。

ボディー・オブジェクトはコピーされることがあるため、その operator() ではボディーの変更はできません。変更された場合は、operator() がオリジナルとコピーのどちらで動作するかによって、parallel_for を起動するスレッドに対する可視性が変わります。この場合の注意点は、parallel_for がボディー・オブジェクトの operator()const として宣言する必要があることです。

サンプルの operator() は、my_a をローカル変数の a にロードします。この必須ではない処理を行う理由は 2 つあります。

ループボディーをボディー・オブジェクトとして記述すると、次のようにテンプレート関数 parallel_for を起動できます。

#include "tbb/tbb.h"
 
void ParallelApplyFoo( float a[], size_t n ) {
    parallel_for(blocked_range<size_t>(0,n), ApplyFoo(a));
}

ここで構築されている blocked_range は、0 から n-1 までの反復空間全体を表し、parallel_for は、各プロセッサーにサブ空間を分割します。コンストラクターの一般的な形式は、blocked_range<T>(begin,end,grainsize) です。T は、値の型を指定します。引数 beginend は、反復空間を半開区間 [begin,end) として STL スタイルで指定します。引数 grainsize の説明は、「チャンクの制御」を参照してください。デフォルトの parallel_for はデフォルトの粒度で適切に動作するヒューリスティックを適用するため、サンプルではデフォルトの粒度 1 を使用しています。

関連情報