1
votes

I want to write a function which employs openMP parallelism but should work whether called from within a parallel region or not. So I used the if clause to suppress parallelism, but this doesn't work as I thought:

#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);
}

which creates the output

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

Thus the first call to func() worked fine: m and s obtain the values 1 as they should, but the second call to func() from within a parallel region did create nested parallelism (with 16 teams of 1 thread each) even though this was suppressed. That is the omp master and omp single directives bind to the preceeding omp parallel if(!p) directive rather than to the outside parallel region.

Of course, one can fix this problem by the following code

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();
}

but this requires an additional function to be defined etc. Is it possible to do this within a single function (and without repeating code)?

2

2 Answers

3
votes

The OpenMP constructs will always bind to the innermost containing construct, even if it isn't active. So I don't think it's possible while retaining the #pragma omp parallel for both code paths (At least with the provided informations about the problem).

Note that it is a good think that it behaves like this, because otherwise the use of conditionals would easily lead to very problematic (read buggy) code. Look at the following example:

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
}

In general a programmer should not need to know the implementation details of the called functions, only their behaviour. So I really shouldn't have to care about whether func creates a nested parallel section or not and under which exact conditions it creates one. However if the barrier would bind to the outer parallel if the inner is inactive this code would be buggy, since some threads of the outer parallel sections encounter the barrier and some don't. Therefore such details stay hidden inside the innermost containing parallel, even if it isn't active.

Personally I have never encountered a situation where I wanted it to behave differently (which would go against information hiding and such), so maybe you should tell us a bit more about what you are trying to accomplish to get better answers.

0
votes

I had a look at the openMP standard. The if clause is actually somewhat misleadingly coined, for the #pragma omp parallel directive is not conditional (as I originally thought). Instead the if clause may restrict the number of threads to 1 thereby suppressing parallelisation.

However, this implies omp single or omp master cannot be used for threadsafe once-per-process writes of global shared variables.