OpenMP任务的递归因子行为?

时间:2018-05-03 09:55:24

标签: c openmp

我想知道以下代码的行为是什么。这是一个使用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)是正确的。我正在寻找这样的运行时图:

Tasks spanning in a recursive loop

我知道一旦第1步完成,线程就会开始处理任务,但是他们正在使用什么?什么变数?他们真的在做什么吗?我们真的能加速像这样的递归计算吗?有时在树中有足够的叶子,我们可以使用多个线程加速合并操作,但在这种情况下,最后只有一片叶子。如果有人能解释这段代码的行为,我真的很感激。 提前谢谢。

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等待!)。

不是。