我使用以下代码来查找使用OpenMP任务构造的数组元素的总和
代码正在产生正确的结果,直到 n = 10000
。
但除此之外,我遇到了分段错误。使用 gdb
,我发现在reduce()
的一次递归调用中发生了错误。输入数组分配没有问题,我已经验证了这一点。
有没有人对问题可能有什么建议?
int reduce (int *arr, unsigned long int n)
{
int x;
if (n <= 0)
return 0;
#pragma omp parallel
{
#pragma omp single nowait
{
#pragma omp task shared(x)
x = reduce(arr, n-1) + arr[n-1];
#pragma omp taskwait
}
}
return x;
}
答案 0 :(得分:2)
看起来你正在通过函数调用的递归深度遇到“堆栈溢出”。请记住,大多数openmp pragma本身都会生成函数,这些函数可能会干扰尾递归优化。
如果你通过valgrind运行,它应该警告你堆栈溢出。
答案 1 :(得分:1)
dlasalle对于实际错误是正确的。
但是,关于如何使用OpenMP任务还有两个基本问题。您在每个递归调用中生成一个并行区域。这意味着您使用嵌套的并行区域。默认情况下,在OpenMP中禁用嵌套并行性,这里没有意义。您希望在递归期间生成的所有任务都由同一个线程池执行。为此,您必须将parallel
/ single
移到递归之外,例如
int reduce_par(int* arr, unsigned long int n)
{
int x;
if (n <= 0)
return 0;
#pragma omp task shared(x)
x = reduce_par(arr, n - 1) + arr[n - 1];
#pragma omp taskwait
return x;
}
int reduce(int* arr, unsigned long int n)
{
#pragma omp parallel
{
#pragma omp single nowait
{
reduce_par(arr, n);
}
}
}
即使这不会出现段错误,即使你拥有无限数量的内核,具有无限的内存带宽和无线程创建开销,这仍然无法从并行化中获得任何性能优势。要弄清楚这一点,请绘制任务及其操作的图形并添加依赖项。尝试在时间轴上安排图表的节点,尊重任务依赖性,并查看是否可以并行计算任何内容。
并行求和的正确解决方案是具有parallel for
子句的reduce
工作共享构造。如果你不得不使用任务,你需要使用分而治之,例如为数组的两半产生两个任务。为了获得合理的性能,您必须以最小的工作负载大小停止创建任务/递归,以便保持开销的可管理性。