之前我曾问过类似的问题,但由于其复杂性,我没有对我的问题给予足够的重视,所以让我重新解释整个问题。
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;
}
我的代码在语义上是否正确;我已经在我的代码中对它进行了测试,它给了我分段错误;
更新:抱歉拼写错误,我编辑了我的代码。
答案 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
注释,并将此代码序列保留在已创建的线程中。
您的代码为我运行,我没有出现分段错误,但我认为这是由您的并行化方法引起的。