避免嵌套的平行区域

时间:2011-12-16 15:32:17

标签: openmp

我想编写一个使用openMP并行性的函数,但是无论是否在并行区域内调用都应该工作。所以我使用if子句来抑制并行性,但这并不像我想的那样工作:

#include <omp.h>
#include <stdio.h>

int m=0,s=0;

void func()
{
  bool p = omp_in_parallel();
  // if clause to suppress nested parallelism
#pragma omp parallel if(!p)
  {
    /* do some massive work in parallel */
#pragma omp master
    ++m;
#pragma omp single
    ++s;
  }
}

int main()
{
  fprintf(stderr,"running func() serial:\n");
  m=s=0;
  func();
  fprintf(stderr," m=%d s=%d\n",m,s);

  fprintf(stderr,"running func() parallel:\n");
  m=s=0;
#pragma omp parallel
  func();
  fprintf(stderr," m=%d s=%d\n",m,s);
}

创建输出

running func() serial:
 m=1 s=1
running func() parallel:
 m=16 s=16

因此,对func()的第一次调用工作正常:ms按原样获取值1,但是从并行区域内第二次调用func()确实创建了嵌套并行性(16个团队,每个团队1个线程),尽管这被抑制了。那就是omp masteromp single指令绑定到前面的omp parallel if(!p)指令而不是外部并行区域。

当然,可以通过以下代码解决此问题

void work()
{
  /* do some massive work in parallel */
#pragma omp master
  ++m;
#pragma omp single
  ++s;
}

void func()
{
  if(omp_in_parallel())
    work();
  else
#pragma omp parallel
    work();
}

但是这需要定义一个额外的功能等。是否可以在单个函数中执行此操作(并且不重复代码)?

2 个答案:

答案 0 :(得分:2)

OpenMP构造将始终绑定到最内层的构造,即使它不是活动的。所以我认为在保留两个代码路径的#pragma omp parallel时是不可能的(至少有关于问题的提供的信息)。

请注意,它很好地认为它的行为与此类似,因为否则使用条件很容易导致非常有问题(读错误)的代码。请看以下示例:

void func(void* data, int size)
{
   #pragma omp parallel if(size > 1024)
   {
       //do some work
       #pragma omp barrier
       //do some more work
   }
}

...
#pragma omp parallel
{
   //create foo, bar, bar varies massively between different threads (so sometimes bigger, sometimes smaller then 1024
   func(foo, bar);
   //do more work
}

通常,程序员不需要知道被调用函数的实现细节,只需知道它们的行为。所以我真的不应该关心func是否创建嵌套的parallel部分以及它创建的部分。但是,如果barrier将绑定到外部parallel,如果内部处于非活动状态,则此代码将出错,因为外部parallel部分的某些线程遇到barrier和某些线程别。因此,这些细节隐藏在最内层的parallel内,即使它不活跃。

就我个人而言,我从未遇到过一种情况,我希望它的行为方式不同(这会违背信息隐藏等),所以也许你应该多告诉我们你想要达到的目标,以获得更好的答案。 / p>

答案 1 :(得分:0)

我看了一下openMP标准。 if子句实际上有点误导,因为#pragma omp parallel指令不是有条件的(正如我原先认为的那样)。相反,if子句可以将线程数限制为1,从而抑制并行化。

但是,这意味着omp singleomp master不能用于线程安全的每进程一次写入全局共享变量。