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 19 Complex Example Illustrating Autoscoping Rules1. 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.
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 21 Fibonacci Exampleint 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 22 Example With single and task Constructsint 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 23 Example With task and taskwait Constructsint 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.