我使用以下代码来使用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;
}
通过函数调用的递归深度,您似乎遇到了"堆栈溢出"。请记住,大多数openmp杂注都会自己生成函数,这可能会干扰尾部递归优化。
如果你通过valgrind,它应该警告你不要溢出堆栈。
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);
}
}
}
即使这不会出错,即使你有无限数量的内核,有无限的内存带宽,没有线程创建开销,这仍然不会从并行化中带来任何性能优势。要弄清楚这一点,请绘制任务及其操作的图形,并添加依赖项。试着根据任务相关性在时间轴上排列图的节点,看看是否有任何东西可以并行计算。
并行求和的正确解决方案是使用reduce
子句的parallel for
工作共享构造。如果必须使用任务,则需要使用分治,例如为数组的两半生成两个任务。为了获得合理的性能,您必须在最小的工作负载大小下停止任务创建/递归,以保持开销可控。