0

以前にも同様の質問をしましたが、問題が複雑であるために十分な注意が払われていないので、問題全体を言い換えさせてください。

moveT ChooseComputerMove(state)
{
 moveT bestMove;
 maxMove(state,bestMove);

 return bestMove;
}

int maxMove(state, bestMove)
{

  int v = -1000;

  #pragma omp parallel for 
  for(int i = 0; i< nMoves; i++)
  {

   moveT move = validMoves[i];

   makemove(state,move);

   #pragma omp task 

   rating = -maxMove(state, move);

    if(rating < v)
      {v=rating ; bestMove = move;}

    #pragma omp taskwait   

    Retractmove(state,move)
 }

 return v;
}

私のコードは意味的に正しいですか?私はすでに自分のコードでテストしており、セグメンテーション違反が発生します。

更新: スペルミスで申し訳ありません。コードを編集しました。

4

2 に答える 2

4

これをコメントと考えてください。明示的なタスク領域が正しく記述されておらず、冗長でもあります。各反復で明示的なタスクを 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
...

これにより、膨大な数の同時スレッドが発生する可能性があることに注意してください。

于 2012-12-29T16:32:28.397 に答える
0

ここで達成しようとしているのは、並列再帰呼び出しを行うことです。これにより、多くの問題が発生する可能性があります。

8 つの物理スレッドがあるとします。nMovesあなたの場合は8を超えないので、8つの並列スレッドで異なる引数を使用して関数を実行しても問題ありませんMaxMove。ただし、これらの呼び出しのそれぞれで別のnMoves-1スレッドを作成しようとしているため、作成する並列スレッドの複雑さは指数関数的です。物理スレッドの数は有限であるため、すべての関数呼び出しを並列に実行してもパフォーマンスが向上するわけではなく、最終的にはすべてのスレッドがビジー状態になります。また、関数呼び出し内で行う計算量と比較して、各スレッドを作成する際のオーバーヘッド コストが高すぎる可能性があります。

#pragma omp task内部と#pragma omp taskwait注釈を削除し、このコードを既に作成されたスレッド内にシリアルのままにします。

あなたのコードは私のために実行され、セグメンテーション違反は発生しませんが、これは並列化へのアプローチが原因であると思います。

于 2012-12-29T12:13:07.103 に答える