ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
Oracle Solaris Studio 12.3: OpenMP API ユーザーガイド Oracle Solaris Studio 12.3 Information Library (日本語) |
この節では、自動スコープ宣言規則がどのように使用されるかを、いくつかの例に示します。
例 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() には並列領域が 1 つあり、この並列領域には、SINGLE 構文とワークシェアリングの DO 構文、CRITICAL 構文がそれぞれ 1 つあります。OpenMP 並列構文のほかに、並列領域内のコードは次のことを行います。
配列 X 内の値を配列 Y にコピーします。
X 内の正の最大値を検出し、その値を M に格納します。
X の一部要素の値を変数 W に蓄積します。
コンパイラはこれらの規則をどのように使用して、並列領域内の変数のスコープを正しく宣言するのでしょうか。
前述の並列領域では、I、N、MM、T、W、M、X、および Y という変数が使用されています。コンパイラは次の情報を決定します。
スカラー I は、ワークシェアリング DO ループのループインデックスです。OpenMP 仕様では、I のスコープは PRIVATE 宣言することが必須です。
スカラー N は並列領域内で読み取られるだけで、データ競合を起こしません。このため、規則 S1 に従って、この変数のスコープは SHARED と宣言されます。
並列領域を実行するスレッドはすべて、スカラー MM の値を 0.0 に設定する文 14 を実行します。この書き込みはデータ競合の原因になるため、規則 S1 は適用されません。この書き込みは、同じスレッド内のあらゆる MM の読み取りの前に起きるため、規則 S2 に従って、MM のスコープは PRIVATE と宣言されます。
同様に、T も PRIVATE とスコープ宣言されます。
スカラー W は 21 行目でいったん読み取られたあとに書き込まれます。このため、S1 および S2 は適用されません。加算は連想および伝達の両方の要素が含まれるため、規則 S3 に従って W のスコープは REDUCTION(+) と宣言されます。
スカラー M は、SINGLE 構文にある文 11 で書き込まれます。この SINGLE 構文の末尾のバリアは、文 11 の書き込みが文 28 の読み取りや文 29 の書き込みと同時に発生しないようにするためのものです。また、文 28 と 29 はどちらも CRITICAL 構文内にあるため、同時に発生しないようになっています。2 つのスレッドが同時に M にアクセスすることはできません。このため、並列領域内での M 読み取りと書き込みがデータ競合を起こすことはなく、規則 S1 に従って、M のスコープは SHARED と宣言されます。
配列 X は領域内では読み取りだけで、書き込みは行われません。このため、この配列のスコープは、規則 A1 に従って SHARED と宣言されます。
配列 Y への書き込みはスレッド間で分散され、2 つのスレッドが Y の同じ要素に書き込むことはありません。データの競合が発生しないため、Y のスコープは、規則 A1 に従って SHARED と宣言されます。
例 6-5 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);
スカラー変数 p および q、およびポインタ変数データは、タスク構文でも並列領域でも読み取り専用です。 そのため、これらは TS1 に従って FIRSTPRIVATE と自動スコープ宣言されます。
例 6-6 フィボナッチのもう 1 つの例
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. }
スカラー n はタスク構文でも並列構文でも読み取り専用です。そのため、n は TS1 に従って FIRSTPRIVATE と自動スコープ宣言されます。
スカラー変数 x および y は、ローカル関数 fib() のローカル変数です。両方のタスクが x と y にアクセスしても、データ競合は起こりません。taskwait があるため、2 つのタスクがまず実行を完了してから、タスクを検出した fib() を実行していたスレッドが fib() を終了します。これは、2 つのタスクの実行中に x と y がアクティブであることを意味します。そのため、x と y は、TS2 に従い、SHARED と自動スコープ宣言されます。
例 6-7 自動スコープ宣言のもう 1 つの例
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. }
この例では、xx は並列領域の private 変数です。チームのスレッドの 1 つが task1 を実行して、xx の初期値を変更します。その後、すべてのスレッドが task2 を検出し、xx を使用して何らかの計算を行います。
task1 では、xx を使用しても、データ競合は発生しません。1 つの構文の終わりに暗黙的なバリアーがあり、このバリアーを出る前に task1 を完了する必要があるので、xx は task1 が実行している間でもアクティブなのです。したがって、TS2 に従い、xx は task1 で SHARED と自動スコープ宣言されます。
task2 では、xx は読み取り専用として使用されます。ただし、xx の使用は、包含する並列構文では読み取り専用ではありません。xx は、並列構文に対しては PRIVATE と事前定義されているので、task2 が実行している間でも xx がアクティブかどうかはわかりません。したがって、TS3 に従い、xx は task2 で FIRSTPRIVATE と自動スコープ宣言されます。
task2 では、yy を使用することでデータ競合が発生し、task2 を実行する各スレッドでは、変数 yy は同じスレッドによる読み取りの前に常に書き込まれます。そのため、TS4 に従い、yy は task2 では PRIVATE と自動スコープ宣言されます。
例 6-8 最後の例
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. }
task 構文では xx は読み取り専用として使用されないので、データ競合が発生します。しかし、タスク領域で x を読み取ると、タスク外で定義された x の値が取得されます(この例では、xx は並列領域に対して SHARED なので、x の定義は実際には並列領域外で行われます)。そのため、TS5 に従い、xx は FIRSTPRIVATE と自動スコープ宣言されます。
task 構文での yy の使用は読み取り専用ではありませんが、データ競合は発生しません。taskwait が発生しているので、yy はタスクの実行中でもアクセスできます。そのため、TS2 に従い、yy は SHARED と自動スコープ宣言されます。