Parallel Lint による OpenMP* の確認

OpenMP を使用してシーケンシャル・アプリケーションから並列アプリケーションへの移行を迅速に行う場合、parallel lint を使用するとアプリケーションの開発時間とデバッグ時間を短縮することができるため、非常に便利です。ここでは、parallel lint を使用して並列アプリケーションを最適化する方法について説明します。parallel lint は、プログラムをスタティックかつグローバルに解析して、並列化における既存の問題と潜在的な問題を診断します。parallel lint の利点の 1 つは、ほかのルーチンに配置されているものを含め、すべての並列領域とワークシェアリング構造を考慮してチェックを行うことです。

1  //**************************************************

2  //* for, sections, single pragmas                  *

3  //* that bind to the same parallel pragma are      *

4  //* not allowed to be nested one inside the other  *

5  //*                                                *

6  //**************************************************

7

8  #include <stdio.h>

9  #include <omp.h>

10

11  void fff(int ii) {

12      printf("We've got i=%d NTR=%d\n",ii, omp_get_thread_num() );

13  }

14

15  void sec2(int i){

16      #pragma omp single

17      fff(i+2);

18  }

19

20  int main(int n) {

21      int i=3;

22      omp_set_num_threads(3);

23      #pragma omp parallel

24      #pragma omp sections

25      {

26          #pragma omp sections

27          sec2(i);

28      }

29      return 0;

30  }

as_12_01.cpp(16): エラー #12200: single プラグマは sections プラグマの

ダイナミック・エクステントでは許可されていません (ファイル:as_12_01.cpp 行:24)。

parallel lint は、プログラム・コンテキスト全体の OpenMP プラグマ を診断する強力なツールです。また、データ依存と競合状態に関するデバッグエラーもチェックします。

1  #include <stdio.h>

2  #include <omp.h>

3

4  int main(void)

5  {

6      int i;

7      int factorial[10];

8

9      factorial[0]=1;

10      #pragma omp parallel for

11      for (i=1; i < 10; i++) {

12

13          factorial[i] = i * factorial[i-1];

14      }

15

16 return 0;

17  }

omp.c(13): 警告 #12246: (ファイル:omp.c 行:13) から (ファイル:omp.c 行:13) までデータフロー依存関係があります。

"factorial" により、並列モードでプログラムが正しく実行されないことがあります。

コンパイルの基本

parallel lint 解析を有効にするには、/Qdiag-enable:sc-parallel[n] オプション (Windows*) または -diag-enable sc-parallel[n] オプション (Linux* および Mac OS*) 指定します。

parallel lint は IA-32 アーキテクチャーおよびインテル® 64 アーキテクチャーのみでサポートされています。

parallel lint には OpenMP オプション /Qopenmp (Windows) または -openmp (Linux および Mac OS) が必要です。このオプションを指定すると、OpenMP プラグマ が処理され、parallel lint で並列化の解析が可能になります。OpenMP を指定しないで parallel lint を使用すると、コンパイラーは次のエラーメッセージを発行します。

コマンドライン・エラー: OpenMP* の並列化オプションが指定されていないため、parallel lint は呼び出されませんでした。

parallel lint を使用する場合、/Qopenmp オプションを追加してください。

Microsoft* Visual Studio* を使用している場合、parallel lint によって作成されるオブジェクト・ファイルとライブラリー・ファイルは製品のビルドには使用できないため、parallel lint 専用のビルド構成を作成してください。

基本チェック

parallel lint は、OpenMP を使用する並列プログラミングの初心者だけでなく、上級開発者にも便利なさまざまな OpenMP チェックを提供します。詳細は、「概要」を参照してください。

次の例では、parallel lint の最も便利な機能について紹介します。

ケース 1: 入れ子された領域

OpenMP プラグマに入れ子された並列領域がある場合、デバッグが困難です。入れ子された並列構造には、さまざまな制限が適用されます。parallel lint は、入れ子された parallel 文 (ほかのファイルに含まれているものも含む) をチェックできます。

次の例では、ワークシェアリング構造は worksharingcriticalordered、または master 構造の内部に入れ子できません。

1  #include <stdio.h>

2  #include <omp.h>

3

4  int fff(int ii)

5  {

6  int rez;

7

8   #pragma omp sections

9    {

10      rez = ii;

11    #pragma omp section

12      rez = ii+2;

13    }

14  return rez;

15  }

16

17

18  int

19  main(int n)

20  {

21  int i;

22

23  omp_set_num_threads(3);

24

25  #pragma omp parallel

26       #pragma omp for ordered

27       for(i=1; i<150; i=i+2) {

28          fff(i);

29          #pragma omp ordered

30          if(i < 50 || i > 52) {

31            printf("i=%d NU=%d \n", i, omp_get_thread_num() )

32         }

33       }

34       return 0;

35  }

omp.c(8): エラー #12200: sections プラグマは、loop プラグマの

ダイナミック・エクステントでは許可されていません (ファイル:omp.c 行:26)。

ケース 2: データ共有属性節

既存のシリアル・アプリケーションをスレッド化する際には、データ共有節を正しい位置に記述する必要があります。parallel lint は、共有節の不適切な使用だけでなく、適切なデータ共有プラグマが不足していないかどうかを確認するのにも役立ちます。

次の例は、「nowait も適用される構造で lastprivate 節が使用されている場合、オリジナルのリスト項目は、最後の反復または記述上において最後の SECTION 構造を実行するスレッドがそのリスト項目を確実に保存するために、バリア同期化が行われるまで未定義のままになる」[OpenMP 標準] という OpenMP 標準の制限を示します。

1  #include <stdio.h>

2  #include <omp.h>

3

4  int main(void) {

5      int last, i;

6      float a[10], b[10];

7

8      for (i=0; i < 10; i++) {

9        b[i] = i*0.5;

10      }

11

12      #pragma omp parallel shared(a,b,last)

13      {

14          #pragma omp for lastprivate(last) nowait

15          for (i=0; i < 10; i++) {

16              a[i] = b[i] * 2;

17              last = a[i];

18          }

19          #pragma omp single

20          printf("%d\n", last);

21      }

22

23      return 0;

24  }

omp.c(20): エラー #12220: NOWAIT ワークシェアリング構文の LASTPRIVATE 変数 "last" が

バリアー同期の前に使用されています。

ケース 3: データの依存性

並列プログラムでは動作が一定ではないため、データの依存性問題をデバッグすることは困難です。parallel lint は、プログラムを実行せずに、プログラム中のデータ依存性問題を特定することができます。

データの依存性を解析するには、診断で重要度 3 の parallel lint を指定します。

1  #include <stdio.h>

2  #include <omp.h>

3

4  int main(void)

5  {

6      int i;

7      float a[100];

8

9      #pragma omp parallel for

10      for (i=0; i < 100; i++) {

11          a[i] = i*0.66;

12      }

13

14      #pragma omp parallel for

15      for (i=1; i < 100; i++) {

16          a[i] = a[i-1]*0.5 + a[i]*0.5;

17      }

18

19      return 0;

20  }

omp.c(16): 警告 #12246: (ファイル:omp.c 行:16) から

(ファイル:omp.c 行:16) までデータフロー依存関係があります。"a" により、並列モードでプログラムが正しく実行されないことがあります。

ケース 4: スレッド・プライベート変数

1  #include <stdio.h>

2  #include <omp.h>

3

4  int a[1000];

5  #pragma omp threadprivate (a)

6

7  int main(int n) {

8      int i;

9      int sum =0;

10

11      #pragma omp parallel for

12      for (i=0; i < 1000; i++) {

13         a[i] = i;

14      }

15      #pragma omp parallel for reduction (+:sum)

16      for (i=10; i < 1000; i++) { // inconsistent init value

17         sum = sum + a[i];

18      }

19      printf("%d\n",sum);

20      return 0;

21  }

omp.cpp(17): エラー #12344: threadprivate 変数 "a" が

初期値の異なるループで使用されています。(ファイル:omp.cpp 行:12) と (ファイル:omp.cpp 行:16) のループを確認してください。