【发布时间】:2013-09-14 04:23:39
【问题描述】:
假设我有一个函数f(i),它依赖于索引i(以及其他无法预先计算的值)。
我想填充一个数组a,以便a[n] = sum(f(i)) from i=0 to n-1。
编辑:在 Hristo Iliev 发表评论后,我意识到我在做的是 cumulative/prefix sum。
这可以写成代码
float sum = 0;
for(int i=0; i<N; i++) {
sum += f(i);
a[i] = sum;
}
现在我想使用 OpenMP 并行执行此操作。我可以使用 OpenMP 执行此操作的一种方法是并行写出 f(i) 的值,然后以串行方式处理依赖关系。如果f(i) 是一个慢速函数,那么这可能会很好地工作,因为非并行循环很简单。
#pragma omp parallel for
for(int i=0; i<N; i++) {
a[i] = f(i);
}
for(int i=1; i<N; i++) {
a[i] += a[i-1];
}
但在没有 OpenMP 的非并行循环的情况下也可以做到这一点。然而,我想出的解决方案很复杂,而且可能很老套。所以我的问题是,是否有一种更简单、不那么复杂的方式来使用 OpenMP 做到这一点?
下面的代码基本上运行我为每个线程列出的第一个代码。结果是给定线程中a 的值是正确的,直到一个常数。我将每个线程的总和保存到带有nthreads+1 元素的数组suma 中。这使我可以在线程之间进行通信并确定每个线程的恒定偏移量。然后我用偏移量更正a[i] 的值。
float *suma;
#pragma omp parallel
{
const int ithread = omp_get_thread_num();
const int nthreads = omp_get_num_threads();
const int start = ithread*N/nthreads;
const int finish = (ithread+1)*N/nthreads;
#pragma omp single
{
suma = new float[nthreads+1];
suma[0] = 0;
}
float sum = 0;
for (int i=start; i<finish; i++) {
sum += f(i);
a[i] = sum;
}
suma[ithread+1] = sum;
#pragma omp barrier
float offset = 0;
for(int i=0; i<(ithread+1); i++) {
offset += suma[i];
}
for(int i=start; i<finish; i++) {
a[i] += offset;
}
}
delete[] suma;
一个简单的测试就是设置f(i) = i。那么解决方案是a[i] = i*(i+1)/2(无穷远处是-1/12)。
【问题讨论】:
-
这就是通常使用 OpenMP 计算前缀和的方式。您可以将
#pragma omp for schedule(static)应用于在a[]上运行的两个循环,而不是手动计算开始和结束索引。 -
@HristoIliev,我认为尽管在实践中 OpenMP 像我一样定义开始和结束,但我不应该假设 OpenMP 会那样做(我想我在你的一篇文章中读过)。代码
for(int i=0; i<(ithread+1); i++)要求在并行循环中,较大的索引值始终对应于较大的线程值。一般情况下是这样吗? -
schedule(static)具有标准保证的特殊属性,例如在某些条件下(在您的情况下满足)可重复分布模式。 -
好的,我想我明白了。我对此提出了一个 SO 问题,因为我认为这是其他人可能想知道的。我有一段时间不确定。
标签: dependencies sum openmp