このセクションでは、自動スコープ宣言規則の使用例をいくつか示します。この規則は、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 と自動スコープ宣言されます。