インテル® C++ コンパイラー 18.0 デベロッパー・ガイドおよびリファレンス
cilk_for ループで処理される 1 回あたりの反復のサイズ (粒度サイズ) を指定します。
#pragma cilk grainsize = expression |
expression |
ループの粒度を指定します。expression はランタイムに評価されます。 粒度をゼロ未満にした場合の結果は不定です。 grainsize プラグマを指定しない場合や grainsize を 0 に指定した場合は、ランタイムシステムが自動的に粒度を計算します。 |
このプラグマを使用して、cilk_for ループの 1 つのインスタンスの粒度を指定できます。
粒度とは、cilk_for ループが 1 つのチャンクとしてスポーンする反復の最大数です。cilk_for 文は、ループを 1 つまたは複数の反復からなるチャンクに分割します。各チャンクはシリアル実行され、ループが実行される間チャンクはスポーンされることで並列に動作します。
個々の反復が特に大きなループや反復ごとに作業量が大きく異なるループでは、このプラグマを使用して粒度を小さくする (1 まで下げる) ことができます。粒度を小さくすると並列性が高まりロードバランスが向上しますが、スケジュールと関数呼び出しのオーバーヘッドが増加します。一般に、より大きく、あまりバランスの取れていないループの反復では、粒度を小さくする必要があります。
ループ本体が短い場合は、粒度を大きくすることでスケジュールのオーバーヘッドを減らすことができます。粒度が 1,000 ~ 2,000 に達すると、反復あたりの作業量が非常に少ない場合でも、cilk_for 文のオーバーヘッドは重要でなくなります。そのため、これ以上粒度を大きくしても利点はありません。粒度が大きすぎると、並列性が低くなり、ロードバランスが適切に行われません。特に、ワーカー (CPU) あたり 1 チャンクになるようにループを分割すべきではありません。スケジューラーがロードバランスを取れなくなり、ほぼ確実にパフォーマンスが低下します。
このプラグマを使用しない場合、デフォルトの grainsize はほとんどのループで適切に動作します。粒度を変更する場合は、必ずパフォーマンス・テストを行って、ループの実行速度が速くなること (遅くならないこと) を確認してください。
粒度を指定しない場合や粒度をゼロにした場合、システムは次のプラグマが指定された場合と同じようにデフォルト値を計算します。
#pragma cilk grainsize = min(2048, ceil(N / (8 * p)))
説明:
N はループの反復回数です。
p はプログラムの実行中に生成されたワーカーの数です。
ほとんどの場合、この式は適切な結果を出力します。反復回数の少ないループ (ワーカーの数 * 8 より少ない場合) では、粒度は 1 に設定され、各反復は並列に動作します。反復回数が多いループ (ワーカーの数 * 16,484 を超える場合) では、粒度は 2,048 に設定されます。
デフォルト値の計算式は、インテル® Cilk™ Plus の仕様には含まれていません。
ワーカーの数に応じて粒度を設定する例 |
---|
#pragma cilk grainsize = n/(410*__cilkrts_get_nworkers()) |
この例は、ワーカーの数に応じて粒度を設定するので、コア数の多いシステムでは粒度が小さくなります (つまり、より多くのワーカーが利用されます)。