Skip Navigation Links | |
Exit Print View | |
Oracle Solaris Studio 12.3: OpenMP API User's Guide Oracle Solaris Studio 12.3 Information Library |
2. Compiling and Running OpenMP Programs
3. Implementation-Defined Behaviors
6. Automatic Scoping of Variables
6.1 Autoscoping Data Scope Clause
6.2 Scoping Rules for a Parallel Construct
6.2.1 Scoping Rules for Scalar Variables
6.2.2 Scoping Rules for Arrays
6.3 Scoping Rules for a task Construct
6.3.1 Scoping Rules for Scalar Variables
6.3.2 Scoping Rules for Arrays
6.4 General Comments About Autoscoping
6.6 Checking the Results of Autoscoping
This section provides some examples to illustrate how the autoscoping rules work.
Example 6-4 A More Complicated Example
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
The function FOO() contains a parallel region, which contains a SINGLE construct, a work-sharing DO construct and a CRITICAL construct. Besides the OpenMP parallel constructs, the code in the parallel region does the following:
Copy the value in array X to array Y
Find the maximum positive value in X, and store it in M
Accumulate the value of some elements of X into variable W.
How does the compiler use the these rules to correctly scope the variables in the parallel region?
The following variables are used in the parallel region, I, N, MM, T, W, M, X, and Y. The compiler will determine the following information:
Scalar I is the loop index of the work-sharing DO loop. The OpenMP specification mandates that I be scoped PRIVATE.
Scalar N is only read in the parallel region and therefore will not cause data race, so it is scoped as SHARED following rule S1.
Any thread executing the parallel region will execute statement 14, which sets the value of scalar MM to 0.0. This write will cause data race, so rule S1 does not apply. The write happens before any read of MM in the same thread, so MM is scoped as PRIVATE according to rule S2.
Similarly, scalar T is scoped as PRIVATE.
Scalar W is read and then written at statement 21, so rules S1 and S2 do not apply. The addition operation is both associative and communicative, therefore, W is scoped as REDUCTION(+) according to rule S3.
Scalar M is written in statement 11 which is inside a SINGLE construct. The implicit barrier at the end of the SINGLE construct ensures that the write in statement 11 will not happen concurrently with either the read in statement 28 or the write in statement 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 region do not cause a data race, and, following rule S1, M is scoped SHARED.
Array X is only read and not written in the region, so it is scoped as SHARED by rule A1.
The writes to array Y is distributed among the threads, and no two threads will write to the same elements of Y. Because no data race occurs, Y is scoped SHARED according to rule A1.
Example 6-5 Example With QuickSort
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 result: 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 region. Therefore, they are autoscoped as FIRSTPRIVATE according to TS1.
Example 6-6 Another Fibbonacci 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 result: 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 the tasks, exits fib(). This implies that x and y will be active while the two tasks are executing. Therefore, x and y are autoscoped as SHARED, according to TS2.
Example 6-7 Another Autoscoping Example
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 result: 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 region. 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 active 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 active 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 A Final Example
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 result: 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 region gets the value of x defined outside the task. (In this example, because xx is SHARED for the parallel region, the definition of x is actually outside the parallel region.) 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.