OpenMP调用for for循环中的递归调用

时间:2012-12-29 05:36:18

标签: recursion parallel-processing openmp

之前我曾问过类似的问题,但由于其复杂性,我没有对我的问题给予足够的重视,所以让我重新解释整个问题。

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;
}

我的代码在语义上是否正确;我已经在我的代码中对它进行了测试,它给了我分段错误;

更新:抱歉拼写错误,我编辑了我的代码。

2 个答案:

答案 0 :(得分:4)

考虑这个评论。您的显式任务区域写错了,除此之外它也是多余的。您在每次迭代中只生成一个显式任务,然后等待它以taskwait结束。但由于任务执行可能会延迟,因此任务本身可能会在具有比较运算符的行之后执行。 E.g。

#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
}

任务生成器循环仅在一个线程中串行运行(因为single指令)。然后在并行区域的末尾有一个隐式任务调度点,因此其他线程开始执行排队任务。 state默认是共享的,必须使firstprivate为任务继承私有版本,否则所有任务都会同时修改同一个全局state变量会导致问题。 state的私有化会导致更高的内存使用量,因此将任务分配到递归树的底部并不是一个好主意。相反,您应该停止在特定级别生成任务并继续执行串行执行。这也可以减少显式任务引起的开销。

需要注意的另一件事 - 除非采取特殊措施,否则只有顶级调用会并行运行。所有其他递归调用将导致嵌套的并行区域,并且默认情况下禁用嵌套并行性,这意味着更深的递归级别将自动串行执行。要启用嵌套并行性,请将环境值OMP_NESTED设置为true,或将以下调用放在程序开头的某处:

#include <omp.h>

...
omp_set_nested(1);
...
// Now call the recursive function
...

请注意,这可能会导致大量并发线程。

答案 1 :(得分:0)

您要在此处尝试实现并行递归调用。这可能会导致很多问题。

假设您有8个物理线程。由于nMoves在你的情况下不超过8,所以在8个并行线程中使用不同的参数运行函数MaxMove是完全没错的。但是,在每个调用中,您都尝试创建另一个nMoves-1线程,因此您创建的并行线程的复杂性是指数级的。由于物理线程的数量是有限的,因此并行运行每个函数调用都不会获得额外的性能,因此最终所有线程都将忙碌。与在函数调用中执行的计算量相比,创建每个线程的开销也可能过高。

我会删除内部#pragma omp task#pragma omp taskwait注释,并将此代码序列保留在已创建的线程中。

您的代码为我运行,我没有出现分段错误,但我认为这是由您的并行化方法引起的。