我想知道以下代码的行为是什么。这是一个使用OpenMP任务并行化的简单因子程序。 当我运行这个程序时,我得到 0 作为输出。我知道缺少 shared(b)子句。这是我从主要方面称呼事实的方式。
#pragma omp parallel
{
#pragma omp single nowait
{
fact_result = fact(4);
}
}
这是实际的阶乘函数。
int fact(int n) {
if(n == 0)
return 1;
int b = 0;
#pragma omp task
b = fact(n - 1);
#pragma omp taskwait
return n * b;
}
如果没有 shared(b),我无法理解为什么结果不正确以及为什么 shared(b)是正确的。我正在寻找这样的运行时图:
我知道一旦第1步完成,线程就会开始处理任务,但是他们正在使用什么?什么变数?他们真的在做什么吗?我们真的能加速像这样的递归计算吗?有时在树中有足够的叶子,我们可以使用多个线程加速合并操作,但在这种情况下,最后只有一片叶子。如果有人能解释这段代码的行为,我真的很感激。 提前谢谢。
答案 0 :(得分:7)
我无法理解为什么结果不正确
根据标准规则2.15.1.1
,任务中b
的数据共享属性为firstprivate
在任务生成构造中,如果不存在默认子句,则a 数据共享属性未由其确定的变量 上述规则首先是私人的。
逻辑上,你的任务看起来像这样:
#pragma omp task
{
int n_private = n;
int b_private = b;
b_private = fact(n_private - 1);
}
firstprivate b
的结果刚刚被丢弃。
我们真的可以加速像这样的递归计算吗?
没有*。你没有真正的树 - 只是一个链。如果树中某些节点上有多个子节点,则只能加速递归计算。
*:如果每个节点都有大量工作,并且该工作的某些部分不依赖于递归调用,则可以进行一些流水线操作。
编辑:
为什么此代码通过添加共享(b)
来工作
因为那时fact
的结果不会被丢弃并从外部范围写入变量b
。
我在理解taskwait的工作方式时遇到了问题。那真的等待队列中的所有任务完成吗?
它等待所有(直接)子任务完成。因为孩子的任务也在等待孩子的任务,所以即使对于孙子女来说也是如此。
你可以这样想:
| time
V
fact(4)
*-----> fact(3)
twait *-----> fact(2)
| twait *-----> fact(1)
| | twait *-----> fact(0)
| | | twait return
| | | done
| | | return
| | done
| | return
| done
| return
done
return
如果是,那么如何完成像b = fact(3)这样的中间任务,而其结果尚未解决(因为它的结果也在taskwait等待!)。
不是。