Parallelize Data - OpenMP Loops with Complex Iteration Control

Sometimes the loop control is spread across complex control flow. Using OpenMP in this situation requires more features than the simple loops. The task body must not access any of the auto variables defined within the annotation site, because they may have been destroyed before or while the task is running. Also, note that variables referenced within the task construct default to firstprivate.

Consider this serial C/C++ code:

extern char a[];
int previousEnd = -1;
 ANNOTATE_SITE_BEGIN(sitename);
   for (int i=0; i<=100; i++) {
      if (!a[i] || i==100) {
         ANNOTATE_TASK_BEGIN(do_something);
             DoSomething(previousEnd+1,i);
         ANNOTATE_TASK_END();
         previousEnd=i;
      }
   }
ANNOTATE_SITE_END();

This is done using the OpenMP task pragma. Without using this feature, such loops are extremely difficult to parallelize. One approach to the adding parallelism to the loop is to simply spawn each call to DoSomething():

 
extern char a[]; 
int previousEnd = -1;
#pragma omp parallel
 {
#pragma omp single
  {
... 
   for (int i=0; i<=100; i++) {
   
     if (!a[i] || i==100) 
     {
     #pragma omp task 
          DoSomething(previousEnd+1,i);
     }
   }
 }
}

It is important that the parameters to DoSomething be passed by value, not by reference, because previousEnd and i can change before or while the spawned task runs.

Consider this serial Fortran code:

. . .
logical(1) a(200)
integer(4) i, previousEnd
...
previousEnd=0
call annotate_site_begin(functions)
do i=1,101
  if a(.not. a(i)) .or. (i .eq. 101) then
  call annotate_task_begin(do_something)
    call DoSomething(previousEnd+1, i)
  call annotate_task_end
  endif
end do
call annotate_site_end

This is easily done using the OpenMP task directive. Without using this feature, such loops are extremely difficult to parallelize. One approach to the parallelize the above loop is simply to spawn each call to DoSomething():

 
. . .
logical(1) a(200)
integer(4) i, previousEnd
...
previousEnd=0
!$omp parallel
!$omp single
do i=1,101
  if a(.not. a(i)) .or. (i .eq. 101) then
  !$omp task
     call DoSomething(previousEnd+1, i)
  !$omp end task
  endif
end do
!$omp end parallel
   

There is no requirement that the omp task pragma or directive be within the surrounding parallel directive's static extent.

For more complete information about compiler optimizations, see our Optimization Notice.