Oracle® Solaris Studio 12.4: OpenMP API ユーザーズガイド

印刷ビューの終了

更新: 2014 年 12 月
 
 

6.8 自動スコープ宣言の例

このセクションでは、自動スコープ宣言規則の使用例をいくつか示します。この規則は、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 構文では、INMMTWMX、および Y という変数が使用されています。コンパイラは次のようにこれらの変数のスコープを決定します。

  • スカラー I は、ワークシェリング do ループのループインデックスです。OpenMP 仕様では、I のスコープは private 宣言することが必須です。

  • スカラー N は並列構文内で読み取られるだけで、データ競合を起こしません。このため、規則 PS1 に従って、この変数のスコープは shared と宣言されます。

  • 並列構文を実行するスレッドはすべて、スカラー MM の値を 0.0 に設定する 14 行目を実行します。この書き込みはデータ競合の原因になるため、規則 PS1 は適用されません。この書き込みは、同じスレッド内の MM の読み取り前に行われるため、規則 PS2 に従って、MM のスコープは private と宣言されます。

  • 同様に、スカラー Tprivate とスコープ宣言されます。

  • スカラー 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 と宣言されます。

使用例 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 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. }

スカラー ntask 構文でも parallel 構文でも読み取り専用です。そのため、nTS1 に従って firstprivate と自動スコープ宣言されます。

スカラー変数 x および y は、関数 fib() のローカル変数です。両方のタスクが x y にアクセスしても、データ競合は起こりません。taskwait があるため、2 つのタスクがまず実行を完了してから、fib() (タスクを検出して生成) を実行していたスレッドが fib() を終了します。これは、2 つのタスクの実行中に xy にアクセスできることを意味します。そのため、xy は、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.   }

この例では、xxparallel 構文の private 変数です。チームのスレッドの 1 つが task1 を実行して、xx の初期値を変更します。その後、すべてのスレッドが task2 を検出し、xx を使用して何らかの計算を行います。

task1 では、xx を使用しても、データ競合は発生しません。single 構文の終わりに暗黙バリアがあり、このバリアを出る前に task1 を完了する必要があるので、xxtask1 の実行中もアクセスできます。したがって、TS2 に従い、xxtask1shared と自動スコープ宣言されます。

task2 では、xx は読み取り専用として使用されます。ただし、xx の使用は、包含する parallel 構文では読み取り専用ではありません。xx は、parallel 構文に対しては private と事前定義されているので、task2 の実行中も xx にアクセスできるかどうかはわかりません。したがって、TS3 に従い、xxtask2firstprivate と自動スコープ宣言されます。

task2 では、yy を使用することでデータ競合が発生し、task2 を実行する各スレッドでは、変数 yy は同じスレッドによる読み取りの前に常に書き込まれます。そのため、TS4 に従い、yytask2 では 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 構文では xxshared であるため)。そのため、TS5 に従い、xxfirstprivate と自動スコープ宣言されます。

task 構文での yy の使用は読み取り専用ではありませんが、データ競合は発生しません。taskwait が発生しているので、yy はタスクの実行中でもアクセスできます。そのため、TS2 に従い、yyshared と自動スコープ宣言されます。