Oracle Solaris Studio 12.2: OpenMP API ユーザーガイド

6.7 自動スコープ宣言の例

自動スコープ宣言規則がどのように使用されるかを、次の例に示します。


例 6–3 より複雑な例


 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 並列構文をすべて無視した場合、並列領域内のコードが行うのは、次のことです。

  1. 配列 X 内の値を配列 Y にコピーします。

  2. X 内の正の最大値を検出し、その値を M に格納します。

  3. X の一部要素の値を変数 W に蓄積します。

コンパイラが前述の規則に従って、この並列領域内の変数に適切なスコープを発見する仕組みをみてみましょう。

前述の並列領域では、I、N、MM、T、W、M、X、および Y という変数が使用されています。コンパイラは次のことを決定します。


例 6–4 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–5 もう 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() を終了します。つまり、x と y は、2 つのタスクが実行している間でも利用可能なのです。そのため、x と y は、TS2 に従い、SHARED と自動スコープ宣言されます。



例 6–6 もう 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 つが xx の初期値を変更します (task1 を実行します)。その後、すべてのスレッドが task2 を検出し、xx が同じ計算を行います。

task1 では、xx を使用しても、データ競合は発生しません。1 つの構文の終わりに暗黙的なバリアーがあり、このバリアーを出る前に task1 を完了する必要があるので、xx は task1 が実行している間でも利用可能なのです。したがって、TS2 により、xx は task1 で SHARED と自動スコープ宣言されます。

task2 では、xx は読み取り専用として使用されます。ただし、xx の使用は、包含する並列構文では読み取り専用ではありません。xx は、並列構文に対しては PRIVATE と事前定義されているので、task2 が実行している間でも xx が利用可能かどうかはわかりません。そのため、TS3 に従い、task2 では xx は FIRSTPRIVATE と自動スコープ宣言されます。

task2 では、yy を使用することでデータ競合が発生し、task2 を実行する各スレッドでは、変数 yy は同じスレッドによる読み取りの前に常に書き込まれます。そのため、TS4 に従い、yy は task2 では PRIVATE と自動スコープ宣言されます。



例 6–7 もう 1 つの例


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 と自動スコープ宣言されます。