我正在尝试用OpenMP任务实现一个n-queens求解器。然而,游戏板设置在主功能中,我将其赋予一个功能。
到目前为止,我有:
bool solve_NQueens(int board[N][N], int col)
{
if (col == N)
{
// #pragma omp critical
// print_solution(board);
#pragma omp critical
SOLUTION_EXISTS = true;
return true;
}
for (int i = 0; i < N; i++)
{
if (can_be_placed(board, i, col))
{
int new_board[N][N];
board[i][col] = 1;
copy(board, new_board);
#pragma omp task firstprivate(col)
solve_NQueens(new_board, col + 1);
board[i][col] = 0;
}
}
return SOLUTION_EXISTS;
}
main
中对该函数的初始调用是:
#pragma omp parallel if(omp_get_num_threads() > 1)
{
#pragma omp single
{
#pragma omp taskgroup
{
solve_NQueens(board, 0);
}
}
}
参数:
// these are global
#define N 14
bool SOLUTION_EXISTS = false;
// these are in main
int board[N][N];
memset(board, 0, sizeof(board));
编译器:
gcc
线程数:4
在得到结果之前,我使用taskgroup
等待所有任务,并且我必须为每个任务复制游戏板(当N
设置为14时,这是一项艰巨的工作,因为有356k个解决方案(。
我试着制作firstprivate
或private
板,在循环内外使用taskwait
,在for循环内部使用taskgroup
等等。我需要一些建议来优化这个逻辑。
注意:在if
子句下的for循环中放入taskgroup
也有帮助,但这比预期的要慢得多。
首先,您的代码中存在一个巨大的问题:solve_NQueens可以递归地提交任务,并且在所有任务实际完成之前返回。您需要在return
之前放置同步,以便SOLUTION_EXISTS
的值有效(使用#pragma omp taskwait
或#pragma omp taskgroup
(。
在性能方面,存在多个问题。
主要的问题是创建了太多的任务:您在每个递归调用中创建一个任务。虽然创建很少的任务会带来所需的并行性,但对其中大部分任务的创建也会带来很大的开销。这个开销可能比执行尾部调用要高得多。可以实现截止策略来减少开销:一般想法是只为第一次递归调用创建任务。在您的情况下,可以使用#pragma omp task
末尾的子句if(col < 3)
来完成此操作。请注意,3
是一个任意值,您可能需要调整此阈值。
此外,board
在任务创建期间被复制(两次((因为它是一个静态数组,OpenMP任务所需的默认变量被隐式复制(。如果代码是使用OpenMP支持编译的,则不需要额外的副本,并且行board[i][col] = 0;
是无用的*(否则将忽略pragma,这是而不是true*(。但是,如果您解决了上述问题,那么引入的额外开销应该不是关键的。