OpenMP,依赖关系图



我做了一些旧的OpenMP练习来练习一下,但我很难找到解决方案。

目标是编写与依赖关系图对应的最简单的 OpenMP 代码。

图表在这里可见:https://i.stack.imgur.com/MAzu1.jpg

第一个很简单。

它对应于以下代码:

#pragma omp parallel
{
#pragma omp simple
{
#pragma omp task
{
A1();
A2();
}
#pragma omp task
{
B1();
B2();
}
#pragma omp task
{
C1();
C2();
}
}
}

第二个仍然很容易。

#pragma omp parallel
{
#pragma omp simple
{
#pragma omp task
{
A1();
}
#pragma omp task
{
B1();
}
#pragma omp task
{
C1();
}
#pragma omp barrier
A2();
B2();
C2();
}
}

现在是最后一个...这让我很烦恼,因为所有函数调用的依赖项数量都不相等。我以为有一个明确说明您应该等待的任务,但我在 OpenMP 文档中找不到我要查找的内容。

如果有人对这个问题有解释,我将不胜感激,因为我已经考虑了一个多月了。

首先,OpenMP 4.5 规范中没有#pragma omp simple。 我想你的意思是#pragma omp single.

如果是这样pragma omp barriersingle区域内是一个坏主意,因为只有一个线程将执行代码并等待不执行该区域的所有其他线程。

此外,在 A2 上的第二个中,B2 和 C2 不再作为任务并行执行。

对于您的尖锐问题: 您正在寻找的似乎是 OpenMP Secification 第 169 页中任务构造的depend子句。

马西米利亚诺对依赖子句及其工作原理有一个很好的解释。

一旦你了解了那里发生了什么,最后一个例子就不是那么复杂了:每个任务Tn都依赖于上一个迭代T-1_n及其邻居(T-1_n-1T-1_n+1(。这种模式被称为雅可比模板。这在偏微分方程求解器中很常见。

正如Henkersmann所说,最简单的选择是使用OpenMP Task的depend子句:

int val_a[N], val_b[N];    
#pragma omp parallel
#pragma omp single
{
int *a = val_a;
int *b = val_b;
for( int t = 0; t < T; ++t ) {
// Unroll the inner loop for the boundary cases
#pragma omp task depend(in:a[0], a[1]) depend(out:b[0])
stencil(b, a, i);
for( int i = 1; i < N-1; ++i ) {
#pragma omp task depend(in:a[i-1],a[i],a[i+1]) 
depend(out:b[i])
stencil(b, a, i);
}
#pragma omp task depend(in:a[N-2],a[N-1]) depend(out:b[N-1])
stencil(b, a, N-1);
// Swap the pointers for the next iteration
int *tmp = a;
a = b;
b = tmp;
}
#pragma omp taskwait
}

如您所见,OpenMP 任务依赖关系是点对点的,这意味着您无法用阵列区域来表示它们。

另一种选择,对于这种特定情况来说更干净一些,是使用屏障间接强制依赖关系:

int a[N], b[N];
#pragma omp parallel
for( int t = 0; t < T; ++t ) {
#pragma omp for
for( int i = 0; i < N-1; ++i ) {
stencil(b, a, i);
}
}

第二种情况在每次内部循环完成时执行同步屏障。同步粒度更粗,因为每个外部循环迭代只有 1 个同步点。但是,如果stencil函数很长且不平衡,则可能值得使用任务。

最新更新