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 の最も便利な機能について紹介します。
OpenMP プラグマに入れ子された並列領域がある場合、デバッグが困難です。入れ子された並列構造には、さまざまな制限が適用されます。parallel lint は、入れ子された parallel 文 (ほかのファイルに含まれているものも含む) をチェックできます。
次の例では、ワークシェアリング構造は worksharing、critical、ordered、または 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)。 |
既存のシリアル・アプリケーションをスレッド化する際には、データ共有節を正しい位置に記述する必要があります。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" が バリアー同期の前に使用されています。 |
並列プログラムでは動作が一定ではないため、データの依存性問題をデバッグすることは困難です。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" により、並列モードでプログラムが正しく実行されないことがあります。 |
例 |
---|
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) のループを確認してください。 |