我参加了一些旧的OpenMP练习,但是我很难找到解决方案。
目标是编写与依赖图对应的最简单的OpenMP代码。
图表在此处可见:http://imgur.com/a/8qkYb
第一个很简单。
它对应于以下代码:
#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文档中寻找的内容。
如果有人对这个问题有解释,我将非常感激,因为我已经考虑了一个多月了。
答案 0 :(得分:0)
首先,OpenMP 4.5规范中没有#pragma omp simple
。
我假设你的意思是#pragma omp single
。
如果是这样pragma omp barrier
在single
区域内是一个坏主意,因为只有一个线程会执行代码并等待所有其他不执行该区域的线程。
此外,在A2的第二个中,B2和C2不再作为任务并行执行。
至于您的实际问题:
您正在寻找的内容似乎是OpenMP Secification pg. 169上任务构造的depend
子句。
对于依赖条款以及Massimiliano for this question的工作方式,有一个很好的解释。
答案 1 :(得分:0)
一旦你理解了那里发生了什么,最后一个例子并不复杂:每个任务Tn
取决于之前的迭代T-1_n
及其邻居(T-1_n-1
和{{1} })。此模式称为Jacobi stencil。它在偏微分方程求解器中很常见。
正如Henkersmann所说,最简单的选择是使用OpenMP Task的T-1_n+1
子句:
depend
正如您所看到的,OpenMP任务依赖是点对点的,这意味着您无法用数组区域来表达它们。
另一个选择,对于这个特定情况更为清晰,是使用障碍间接地强制执行依赖:
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
}
每次内循环结束时,第二种情况都会执行同步屏障。在每个外部循环迭代只有1个同步点的意义上,同步粒度更粗糙。但是,如果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);
}
}
函数很长且不平衡,则可能值得使用任务。