このセクションでは、自動スコープ宣言規則の使用例をいくつか示します。この規則は、parallel 構文のスコープ宣言の規則およびtask 構文内のスカラー変数のスコープ宣言の規則に記載されています。
使用例 6-4 自動スコープ宣言の規則を示す複雑な例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
この例では、関数 FOO() には parallel 構文が 1 つあり、この parallel 構文には、single 構文とワークシェアリングの do 構文、critical 構文がそれぞれ 1 つあります。
parallel 構文では、I、N、MM、T、W、M、X、および Y という変数が使用されています。コンパイラは次のようにこれらの変数のスコープを決定します。
スカラー I は、ワークシェリング do ループのループインデックスです。OpenMP 仕様では、I のスコープは private 宣言することが必須です。
スカラー N は並列構文内で読み取られるだけで、データ競合を起こしません。このため、規則 PS1 に従って、この変数のスコープは shared と宣言されます。
並列構文を実行するスレッドはすべて、スカラー MM の値を 0.0 に設定する 14 行目を実行します。この書き込みはデータ競合の原因になるため、規則 PS1 は適用されません。この書き込みは、同じスレッド内の MM の読み取り前に行われるため、規則 PS2 に従って、MM のスコープは private と宣言されます。
同様に、スカラー T も private とスコープ宣言されます。
スカラー W は 21 行目でいったん読み取られたあとに書き込まれます。このため、PS1 および PS2 は適用されません。加算は連想および伝達の両方の要素が含まれるため、規則 PS3 に従って W のスコープは reduction(+) と宣言されます。
スカラー M は、single 構文にある 11 行目で書き込まれます。この single 構文の末尾の暗黙バリアは、11 行目の書き込みが 28 行目の読み取りや 29 行目の書き込みと同時に発生しないようにするためのものです。また、28 行目と 29 行目はどちらも critical 構文内にあるため、同時に発生しないようになっています。2 つのスレッドが同時に M にアクセスすることはできません。このため、parallel 構文内での M 読み取りと書き込みがデータ競合を起こすことはなく、規則 S1 に従って、M のスコープは shared と宣言されます。
配列 X は構文内では読み取りだけで、書き込みは行われません。このため、この配列のスコープは、規則 PA1 に従って shared と宣言されます。
配列 Y への書き込みはスレッド間で分散され、2 つのスレッドが Y の同じ要素に書き込むことはありません。データの競合が発生しないため、Y のスコープは、規則 PA1 に従って shared と宣言されます。
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);
スカラー変数 p および q、およびポインタ変数データは、task 構文でも parallel 構文でも読み取り専用です。そのため、これらは TS1 に従って firstprivate と自動スコープ宣言されます。
使用例 6-6 フィボナッチの例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. }
スカラー n は task 構文でも parallel 構文でも読み取り専用です。そのため、n は TS1 に従って firstprivate と自動スコープ宣言されます。
スカラー変数 x および y は、関数 fib() のローカル変数です。両方のタスクが x と y にアクセスしても、データ競合は起こりません。taskwait があるため、2 つのタスクがまず実行を完了してから、fib() (タスクを検出して生成) を実行していたスレッドが fib() を終了します。これは、2 つのタスクの実行中に x と y にアクセスできることを意味します。そのため、x と y は、TS2 に従い、shared と自動スコープ宣言されます。
使用例 6-7 single 構文および task 構文を使用した例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. }
この例では、xx は parallel 構文の private 変数です。チームのスレッドの 1 つが task1 を実行して、xx の初期値を変更します。その後、すべてのスレッドが task2 を検出し、xx を使用して何らかの計算を行います。
task1 では、xx を使用しても、データ競合は発生しません。single 構文の終わりに暗黙バリアがあり、このバリアを出る前に task1 を完了する必要があるので、xx は task1 の実行中もアクセスできます。したがって、TS2 に従い、xx は task1 で shared と自動スコープ宣言されます。
task2 では、xx は読み取り専用として使用されます。ただし、xx の使用は、包含する parallel 構文では読み取り専用ではありません。xx は、parallel 構文に対しては private と事前定義されているので、task2 の実行中も xx にアクセスできるかどうかはわかりません。したがって、TS3 に従い、xx は task2 で firstprivate と自動スコープ宣言されます。
task2 では、yy を使用することでデータ競合が発生し、task2 を実行する各スレッドでは、変数 yy は同じスレッドによる読み取りの前に常に書き込まれます。そのため、TS4 に従い、yy は task2 では private と自動スコープ宣言されます。
使用例 6-8 task 構文および taskwait 構文を使用した例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. }
task 構文では xx は読み取り専用として使用されないので、データ競合が発生します。ただし、タスクの x の読み取りではタスクの外部で定義された x の値が取得されます (parallel 構文では xx は shared であるため)。そのため、TS5 に従い、xx は firstprivate と自動スコープ宣言されます。
task 構文での yy の使用は読み取り専用ではありませんが、データ競合は発生しません。taskwait が発生しているので、yy はタスクの実行中でもアクセスできます。そのため、TS2 に従い、yy は shared と自動スコープ宣言されます。