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

第 7 章 スコープチェック

自動スコープ宣言を使用すると、変数をどのようにスコープ宣言するかをプログラマが決定できます。ただし、複雑なプログラムの場合、自動スコープ宣言が実行されなかったり、自動スコープ宣言の結果が予期しないものになったりすることがあります。不正なスコープ宣言により、目立たないが深刻な問題が多数発生することがあります。たとえば、一部の変数を SHARED として不正にスコープ宣言するとデータ競合が起きるという可能性や、変数のスレッド固有化を正しく行わないと構文外でその変数が未定義の値になるという可能性があります。

Solaris Studio C、C++、および Fortran のコンパイラは、コンパイル時のスコープチェック機能を備えており、OpenMP プログラムの変数が正しくスコープ宣言されたかどうかがコンパイラによって確認されます。

スコープチェックを行えば、コンパイラの機能に応じて、データ競合、不適切な変数のスレッド固有化や縮約、およびその他のスコープ宣言上の不具合など、潜在的な問題を検出することができます。スコープチェック時には、プログラマによって指定されたデータ共有属性、コンパイラによって決定された暗黙的なデータ共有属性、および自動スコープ宣言の結果が、コンパイラによって確認されます。

7.1 スコープチェック機能の使用

スコープチェックを有効にするには、-xvpara および -xopenmp オプションを使用するとともに最適化レベルを -xO3 かそれ以上に設定してから OpenMP プログラムをコンパイルする必要があります。スコープチェックは、プログラムが -xopenmp=noopt でコンパイルされただけでは動作しません。最適化レベルが -xO3 未満の場合、コンパイラは警告メッセージを発行し、スコープチェックを行いません。

スコープチェック時には、コンパイラはすべての OpenMP 構文を確認します。いくつかの変数のスコープ宣言に問題がある場合は、コンパイラは警告メッセージを発行します。場合によっては、正しいデータ共有属性節が提案されます。

次に例を示します。


例 7–1 スコープチェック


% cat t.c

#include <omp.h>
#include <string.h>

int main()
{
  int g[100], b, i;

  memset(g, 0, sizeof(int)*100);

  #pragma omp parallel for shared(b)
  for (i = 0; i < 100; i++)
  {
    b += g[i];
  }

  return 0;
}

%  cc -xopenmp -xO3 -xvpara source1.c
"source1.c", line 10: Warning: inappropriate scoping
         variable 'b' may be scoped inappropriately as 'shared'
         . write at line 13 and write at line 13 may cause data race

"source1.c", line 10: Warning: inappropriate scoping
         variable 'b' may be scoped inappropriately as 'shared'
         . write at line 13 and read at line 13 may cause data race

コンパイラは、最適化レベルが -xO3 未満の場合はスコープチェックを行いません。


%  cc -xopenmp=noopt  -xvpara source1.c
 "source1.c", line 10: Warning: Scope checking under vpara compiler 
option is supported with optimization level -xO3 or higher.
 Compile with a higher optimization level to enable this feature

より複雑な例:


例 7–2 source2


% cat source2.c

#include <omp.h>

int main()
{
  int g[100];
  int r=0, a=1, b, i;

  #pragma omp parallel for private(a) lastprivate(i) reduction(+:r)
  for (i = 0; i < 100; i++)
  {
    g[i] = a;
    b = b + g[i];
    r = r * g[i];
  }

  a = b;
  return 0;
}

% cc -xopenmp -xO3 -xvpara source2.c
"source2.c", line 8: Warning: inappropriate scoping
        variable 'r' may be scoped inappropriately as 'reduction'
        . reference at line 13 may not be a reduction of the specified type

"source2.c", line 8: Warning: inappropriate scoping
        variable 'a' may be scoped inappropriately as 'private'
        . read at line 11 may be undefined
        . consider 'firstprivate'

"source2.c", line 8: Warning: inappropriate scoping
        variable 'i' may be scoped inappropriately as 'lastprivate'
        . value defined inside the parallel construct is not used outside
        . consider 'private'

"source2.c", line 8: Warning: inappropriate scoping
        variable 'b' may be scoped inappropriately as 'shared'
        . write at line 12 and write at line 12 may cause data race

"source2.c", line 8: Warning: inappropriate scoping
        variable 'b' may be scoped inappropriately as 'shared'
        . write at line 12 and read at line 12 may cause data race

上記の疑似的な例は、スコープチェックによって検出される典型的なスコープ宣言エラーを示します。

  1. 縮約変数として r が指定され、この変数の演算は + となっているが、実際に行われるべき演算は * です。

  2. PRIVATE として a が明示的にスコープ宣言されています。PRIVATE 変数には初期値がないので、行 11 の a への参照では、何らかのガベージ値が取得されることがあります。コンパイラはこの問題を指摘し、FIRSTPRIVATE としてスコープ宣言することをプログラマに提案します。

  3. 変数 i はループインデックス変数です。ループインデックスがループ後に使用される場合、プログラマはこれを LASTPRIVATE として指定することもあります。 しかし、これは上記の例には当てはまりません。i はループ後にはまったく参照されていません。コンパイラは警告を発行し、iPRIVATE としてスコープ宣言するようプログラマに提案します。PRIVATELASTPRIVATE の代わりに使用すると、パフォーマンスが向上します。

  4. プログラマは、変数 b に対してはデータ共有属性を明示的に指定しません。OpenMP 仕様 3.0 の 79 ページ、27 - 28 行によると、bSHARED として暗黙的にスコープ宣言されます。ただし、bSHARED としてスコープ宣言すると、データ競合が発生します。b の正しいデータ共有属性は REDUCTION です。

7.2 制限事項