Oracle® Solaris Studio 12.4: OpenMP API User's Guide

Exit Print View

Updated: December 2014
 
 

6.8 Autoscoping Examples

This section provides some examples to illustrate how the autoscoping rules work. The rules are described in Scoping Rules for a parallel Construct and Scoping Rules for Scalar Variables in a task Construct.

Example 6-4  Complex Example Illustrating Autoscoping Rules
 1.      REAL FUNCTION FOO (N, X, Y)
 2.      INTEGER       N, I
 3.      REAL          X(*), Y(*)
 4.      REAL          W, MM, M
 5.
 6.      W = 0.0
 7.
 8. C$OMP PARALLEL DEFAULT(__AUTO)
 9.
10. C$OMP SINGLE
11.       M = 0.0
12. C$OMP END SINGLE
13.
14.       MM = 0.0
15.
16. C$OMP DO
17.       DO I = 1, N
18.          T = X(I)
19.          Y(I) = T
20.          IF (MM .GT. T) THEN
21.             W = W + T
22.             MM = T
23.          END IF
24.       END DO
25. C$OMP END DO
26.
27. C$OMP CRITICAL
28.       IF ( MM .GT. M ) THEN
29.          M = MM
30.       END IF
31. C$OMP END CRITICAL
32.
33. C$OMP END PARALLEL
34.
35.      FOO = W - M
36.
37.      RETURN
38.      END

In this example, function FOO() contains a parallel construct, which contains a single construct, a worksharing do construct, and a critical construct.

The variables I, N, MM, T, W, M, X, and Y, are used in the parallel construct. The compiler determines the scopes of these variables as follows:

  • Scalar I is the loop index of the worksharing do loop. The OpenMP specification mandates that I be scoped private.

  • Scalar N is only read in the parallel construct and therefore will not cause a data race, so it is scoped as shared following rule PS1.

  • Any thread executing the parallel construct will execute line 14, which sets the value of scalar MM to 0.0. This write will cause a data race, so rule PS1 does not apply. The write happens before any read of MM in the same thread, so MM is scoped as private according to rule PS2.

  • Similarly, scalar T is scoped as private.

  • Scalar W is read and then written at line 21, so rules PS1 and PS2 do not apply. The addition operation is both associative and communicative, therefore, W is scoped as reduction(+) according to rule PS3.

  • Scalar M is written at line 11 which is inside a single construct. The implicit barrier at the end of the single construct ensures that the write at line 11 will not happen concurrently with either the read at line 28 or the write at line 29, and the latter two will not happen at the same time because both are inside the same critical construct. No two threads can access M at the same time. Therefore, the writes and reads of M in the parallel construct do not cause a data race, and, following rule S1, M is scoped as shared.

  • Array X is only read and not written in the construct, so it is scoped as shared by rule PA1.

  • The writes to array Y are distributed among the threads, and no two threads will write to the same element of Y. Because no data race occurs, Y is scoped as shared according to rule PA1.

Example 6-5  QuickSort Example
static void par_quick_sort (int p, int r, float *data)
{
   if (p < r) 
   {
      int q = partition (p, r, data);  
      
      #pragma omp task default(__auto) if ((r-p)>=low_limit)
      par_quick_sort (p, q-1, data);

      #pragma omp task default(__auto) if ((r-p)>=low_limit)
      par_quick_sort (q+1, r, data);
   }
}

int main ()
{
  ...
  #pragma omp parallel
  {
     #pragma omp single nowait
     par_quick_sort (0, N-1, &Data[0]);
  }
  ...
}

er_src shows the following compiler commentary:

      Source OpenMP region below has tag R1
      Variables autoscoped as FIRSTPRIVATE in R1: p, q, data
      Firstprivate variables in R1: data, p, q
        47. #pragma omp task default(__auto) if ((r-p)>=low_limit)
        48. par_quick_sort (p, q-1, data);
      
      Source OpenMP region below has tag R2
      Variables autoscoped as FIRSTPRIVATE in R2: q, r, data
      Firstprivate variables in R2: data, q, r
        49. #pragma omp task default(__auto) if ((r-p)>=low_limit)
        50. par_quick_sort (q+1, r, data);

The scalar variables p and q, and the pointer variable data, are read-only in the task construct, and read-only in the parallel construct. Therefore, they are autoscoped as firstprivate according to TS1.

Example 6-6  Fibonacci Example
int fib (int n)
{
   int x, y;
   if (n < 2) return n;

   #pragma omp task default(__auto)
   x = fib(n - 1);

   #pragma omp task default(__auto)
   y = fib(n - 2);

   #pragma omp taskwait
   return x + y;
}

er_src shows the following compiler commentary:

   Source OpenMP region below has tag R1
   Variables autoscoped as SHARED in R1: x
   Variables autoscoped as FIRSTPRIVATE in R1: n
   Shared variables in R1: x
   Firstprivate variables in R1: n
    24.         #pragma omp task default(__auto) /* shared(x) firstprivate(n) */
    25.         x = fib(n - 1);
   
   Source OpenMP region below has tag R2
   Variables autoscoped as SHARED in R2: y
   Variables autoscoped as FIRSTPRIVATE in R2: n
   Shared variables in R2: y
   Firstprivate variables in R2: n
    26.         #pragma omp task default(__auto) /* shared(y) firstprivate(n) */
    27.         y = fib(n - 2);
    28. 
    29.         #pragma omp taskwait
    30.         return x + y;
    31. }

Scalar n is read-only in the task constructs and read-only in the parallel construct. Therefore, n is autoscoped as firstprivate, according to TS1.

Scalar variables x and y are local variables of function fib(). Accesses to x and y in both tasks are free of data race. Because there is a taskwait, the two tasks will complete execution before the thread executing fib() (which encountered and generated the tasks) exits fib(). This implies that x and y will be accessible while the two tasks are executing. Therefore, x and y are autoscoped as shared, according to TS2.

Example 6-7  Example With single and task Constructs
int main(void)
{
  int yy = 0;

  #pragma omp parallel default(__auto) shared(yy)
  {
    int xx = 0;

    #pragma omp single
    {
       #pragma omp task default(__auto) // task1
       {
          xx = 20;
       }
    }

    #pragma omp task default(__auto) // task2
    {
       yy = xx;
    }
  }

  return 0;
}

er_src shows the following compiler commentary:

   Source OpenMP region below has tag R1
   Variables autoscoped as PRIVATE in R1: xx
   Private variables in R1: xx
   Shared variables in R1: yy
     7.   #pragma omp parallel default(__auto) shared(yy)
     8.   {
     9.     int xx = 0;
    10.

   Source OpenMP region below has tag R2
    11.     #pragma omp single
    12.     {

   Source OpenMP region below has tag R3
   Variables autoscoped as SHARED in R3: xx
   Shared variables in R3: xx
    13.        #pragma omp task default(__auto) // task1
    14.        {
    15.           xx = 20;
    16.        }
    17.     }
    18.

   Source OpenMP region below has tag R4
   Variables autoscoped as PRIVATE in R4: yy
   Variables autoscoped as FIRSTPRIVATE in R4: xx
   Private variables in R4: yy
   Firstprivate variables in R4: xx
    19.     #pragma omp task default(__auto) // task2
    20.     {
    21.        yy = xx;
    22.     }
    23.   }

In this example, xx is a private variable in the parallel construct. One of the threads in the team modifies its initial value of xx by executing task1. Then all of the threads encounter task2, which uses xx to do some computation.

In task1, the use of xx is free of data race. Because an implicit barrier is at the end of the single construct and task1 should complete before exiting this barrier, xx will be accessible while task1 is executing. Therefore, according to TS2, xx is autoscoped as shared on task1.

In task2, the use of xx is read-only. However, the use of xx is not read-only in the enclosing parallel construct. Because xx is predetermined as private for the parallel construct, whether xx will be accessible while task2 is executing is not certain. Therefore, according to TS3, xx is autoscoped firstprivate on task2.

In task2, the use of yy is not free of data race, and in each thread executing task2, the variable yy is always written before being read by the same thread. So, according to TS4, yy is autoscoped private on task2.

Example 6-8  Example With task and taskwait Constructs
int foo(void)
{
  int xx = 1, yy = 0;

  #pragma omp parallel shared(xx,yy)
  {
    #pragma omp task default(__auto)
    {
       xx += 1;

       #pragma omp atomic
       yy += xx;
    }

    #pragma omp taskwait
  }
  return 0;
}

er_src shows the following compiler commentary:

   Source OpenMP region below has tag R1
   Shared variables in R1: yy, xx
     5.   #pragma omp parallel shared(xx,yy)
     6.   {

   Source OpenMP region below has tag R2
   Variables autoscoped as SHARED in R2: yy
   Variables autoscoped as FIRSTPRIVATE in R2: xx
   Shared variables in R2: yy
   Firstprivate variables in R2: xx
     7.     #pragma omp task default(__auto)
     8.     {
     9.        xx += 1;
    10.
    11.        #pragma omp atomic
    12.        yy += xx;
    13.     }
    14.
    15.     #pragma omp taskwait
    16.   }

The use of xx in the task construct is not read-only and is not free of data race. However the read of x in the task gets the value of x defined outside the task (because xx is shared in the parallel construct) Therefore, according to TS5, xx is autoscoped as firstprivate.

The use of yy in the task construct is not read-only but is free of data race. yy will be accessible while the task is executing because there is a taskwait. Therefore, according to TS2, yy is autoscoped as shared.