これをコメントと考えてください。明示的なタスク領域が正しく記述されておらず、冗長でもあります。各反復で明示的なタスクを 1 つだけ生成し、taskwait
. ただし、タスクの実行が延期される可能性があるため、比較演算子を含む行の後にタスク自体が実行される可能性があります。例えば
#pragma omp task
rating = -maxMove(state, move); // <-- This stmt is the body of the task
if(rating < v)
{v=rating ; bestMove = move;}
#pragma omp taskwait
task
タスクの本体は、プラグマに続く次のブロックです。
実際の実行フローは次のようになります。
- タスクは作成されますが、キューに入れられます。
if(rating < v) ....
ステートメントが実行されます。
- コンストラクトがヒットし、
taskwait
すべてのタスクが処理されるまで実行がブロックされます。これにより、タスクの実行が開始されます。
- 新しい評価が計算されますが、ステートメントが既に実行され
v
ているため、 の値は更新されません。if
両方のステートメントをtask
コンストラクトにtaskwait
配置し、並列領域の最後にあることが暗示されているため、を削除することをお勧めします。ベクトルをmakemove
変更するため、単一のタスク プロデューサーパターンを使用することをお勧めします。state
#pragma omp parallel
{
#pragma omp single nowait
for(int i = 0; i < nMoves; i++)
{
moveT move = validMoves[i], opponentsBestMove;
makemove(state, move);
#pragma omp task firstprivate(state)
{
// Declare rating here
int rating = -maxMove(state, opponentsBestMove);
#pragma omp critical
if (rating > v) { v = rating; bestMove = move; }
}
Retractmove(state, move)
}
// An implicit taskwait here
}
タスク プロデューサー ループは、1 つのスレッドのみでシリアルに実行されます (single
ディレクティブのため)。次に、並列領域の最後に暗黙のタスク スケジューリング ポイントがあるため、他のスレッドがキューに入れられたタスクの実行を開始します。はstate
デフォルトで共有されておりfirstprivate
、タスクがプライベート バージョンを継承するために作成する必要があります。そうしないと、すべてのタスクが同じグローバルstate
変数を同時に変更し、問題が発生する可能性があります。をプライベート化するstate
と、メモリ使用量が大幅に増加するため、再帰ツリーの最下部までタスクを実行できるようにすることはお勧めできません。むしろ、特定のレベルでタスクの生成を停止し、シリアル実行を続行する必要があります。これにより、明示的なタスクによって引き起こされるオーバーヘッドも削減されます。
注意すべきもう 1 つの点 - 特別な措置を講じない限り、最上位の呼び出しのみが並列で実行されます。他のすべての再帰呼び出しはネストされた並列領域になり、ネストされた並列処理はデフォルトで無効になっています。これは、より深い再帰レベルが自動的にシリアルに実行されることを意味します。ネストされた並列処理を有効にするには、環境値OMP_NESTED
を に設定するtrue
か、次の呼び出しをプログラムの先頭に配置します。
#include <omp.h>
...
omp_set_nested(1);
...
// Now call the recursive function
...
これにより、膨大な数の同時スレッドが発生する可能性があることに注意してください。