OpenMP を使用してシーケンシャル・アプリケーションから並列アプリケーションへの移行を迅速に行う場合、parallel lint を使用するとアプリケーションの開発時間とデバッグ時間を短縮することができるため、非常に便利です。ここでは、parallel lint を使用して並列アプリケーションを最適化する方法について説明します。parallel lint は、プログラムをスタティックかつグローバルに解析して、並列化における既存の問題と潜在的な問題を診断します。parallel lint の利点の 1 つは、ほかのルーチンに配置されているものを含め、すべての並列領域とワークシェアリング構造を考慮してチェックを行うことです。
例 |
---|
1 parameter (N = 100) 2 real, dimension(N) :: x,y 3 4 !$OMP PARALLEL DEFAULT(SHARED) 5 !$OMP SECTIONS 6 !$OMP SECTION 7 do i = 1, N 8 call work(x, N, i) 9 call output(x, N) 10 end do 11 !$OMP SECTION 12 call work(y, N, N) 13 call output(y, N) 14 !$OMP END SECTIONS 15 !$OMP END PARALLEL 16 print *, x, y 17 end 18 19 20 subroutine work(x, N, i) 21 real, dimension(N) :: x 22 x(i) = i*10.0 23 end subroutine work 24 25 subroutine output(x, N) 26 real, dimension(N) :: x 27 !$OMP SINGLE 28 print *, x 29 !$OMP END SINGLE tst.f(27): エラー #12200: SINGLE 宣言子は SECTIONS 宣言子の ダイナミック・エクステントでは許可されていません (ファイル:tst.f 行:5)。 |
parallel lint は、プログラム・コンテキスト全体の OpenMP 宣言子 を診断する強力なツールです。また、データ依存と競合状態に関するデバッグエラーもチェックします。
例 |
---|
1 parameter (N = 10) 2 integer i 3 integer, dimension(N) :: factorial 4 5 factorial(1) = 1 6 !$OMP PARALLEL DO 7 do i = 2, N 8 factorial(i) = i * factorial(i-1) 9 end do 10 print *, factorial 11 end tst.f(8): 警告 #12246: (ファイル:tst.f 行:8) から (ファイル:tst.f 行:8) までデータフロー依存関係があります。 "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 parameter (N = 10) 2 real, dimension(N,N) :: x, y, z 3 x = 1.0 4 y = 2.0 5 !$OMP PARALLEL DEFAULT(SHARED) 6 !$OMP MASTER 7 call work(x, y, z, N) 8 !$OMP END MASTER 9 !$OMP END PARALLEL 10 print *, z 11 end 12 13 subroutine work(x, y, z, N) 14 real, dimension(N,N) :: x, y, z 15 !$OMP DO 16 do i = 1, N 17 do j = 1, N 18 z(i,j) = x(i,j) + y(j,i) 19 end do 20 end do 21 end subroutine work tst.f(15): エラー #12200: LOOP 宣言子は MASTER 宣言子の ダイナミック・エクステントでは許可されていません (ファイル:tst.f 行:6)。 |
既存のシリアル・アプリケーションをスレッド化する際には、データ共有節を正しい位置に記述する必要があります。parallel lint は、共有節の不適切な使用だけでなく、適切なデータ共有宣言子が不足していないかどうかを確認するのにも役立ちます。
次の例は、「NOWAIT も適用される構造で LASTPRIVATE 節が使用されている場合、オリジナルのリスト項目は、最後の反復または記述上において最後の SECTION 構造を実行するスレッドがそのリスト項目を確実に保存するために、バリア同期化が行われるまで未定義のままになる」[OpenMP 標準] という OpenMP 標準の制限を示します。
例 |
---|
1 integer, parameter :: N=10 2 integer last, i 3 real, dimension(N) :: a, b, c 4 b = 10.0 5 c = 50.0 6 !$OMP PARALLEL SHARED(a, b, c, last) 7 !$OMP DO LASTPRIVATE(last) 8 do i = 1, N 9 a(i) = b(i) + c(i) 10 last = i 11 end do 12 !$OMP END DO NOWAIT 13 !$OMP SINGLE 14 call sub(last) 15 !$OMP END SINGLE 16 !$OMP END PARALLEL 17 end 18 19 subroutine sub(last) 20 integer last 21 print *, last 22 end subroutine sub tst.f(14): エラー #12220: NOWAIT ワークシェアリング構文の LASTPRIVATE 変数 "LAST" が バリアー同期の前に使用されています。 |
次の例は、「並列領域の実行中に割り当てられたプライベート・ポインターは、メモリーリークを回避するために、並列領域が終わる前にプログラムによって明示的に割り当てを解除する必要がある」という OpenMP 標準の制限を示します。
例 |
---|
1 integer :: OMP_GET_THREAD_NUM 2 integer, pointer :: ptr 3 integer, pointer :: a(:) 4 5 call OMP_SET_NUM_THREADS(2) 6 allocate(ptr) 7 allocate(a(2)) 8 ptr = 5 9 print *, ptr 10 !$OMP PARALLEL PRIVATE(ptr) SHARED(a) 11 allocate(ptr) 12 ptr = 3 13 !$OMP CRITICAL 14 a(OMP_GET_THREAD_NUM()+1) = ptr 15 !$OMP END CRITICAL 16 !$OMP END PARALLEL 17 print *, a 18 end as_44_1.f(11): エラー #12359: メモリーリークを回避するために、private ポインター "PTR" は 並列領域 (ファイル:as_44_1.f 行:10) の前にプログラムで明示的に割り当て解除する必要があります。 |
並列プログラムでは動作が一定ではないため、データの依存性問題をデバッグすることは困難です。parallel lint は、プログラムを実行せずに、プログラム中のデータ依存性問題を特定することができます。
データの依存性を解析するには、診断で重要度 3 の parallel lint を指定します。
例 |
---|
1 integer i, a(4) 2 !$OMP PARALLEL DO SHARED(i) NUM_THREADS(4) 3 do i=1,4 4 a(i) = loc(i) 5 end do 6 !$OMP END PARALLEL DO 7 print *,a 8 end tst.f(3): 警告 #12246: (ファイル:tst.f 行:3) から (ファイル:tst.f 行:3) までデータフロー依存関係があります。 "I" により、並列モードでプログラムが正しく実行されないことがあります。 |
例 |
---|
1 integer a(1000) 2 !$OMP THREADPRIVATE(a) 3 integer i, sum 4 5 !$OMP PARALLEL DO 6 do i=1,1000 7 a(i) = i 8 end do 9 !$OMP END PARALLEL DO 10 !$OMP PARALLEL DO REDUCTION(+:sum) 11 do i=10,1000 12 sum = sum + a(i) 13 end do 14 !$OMP END PARALLEL DO 15 print *,sum 16 end tst.f(12): エラー #12344: THREADPRIVATE 変数 "A" が 初期値の異なるループで使用されています。 (ファイル:tst.f 行:6) と (ファイル:tst.f 行:11) のループを確認してください。 |
リダクションは並列プログラミングで広く使われていますが、さまざまな制限があります。parallel lint は、リダクションに関する潜在的な問題を回避するのに役立ちます。この例では、REDUCTION 節に記述する変数は、囲まれたコンテキスト内で SHARED でなければならないという OpenMP API の明示的な制限を示します。
例 |
---|
1 integer i, j 2 real a 3 4 !$OMP PARALLEL PRIVATE(a) 5 do i = 1, 10 6 call sub(a,i) 7 end do !$OMP SINGLE ! 9 print *, a 10 !$OMP END SINGLE 11 !$OMP END PARALLEL 12 end 13 14 subroutine sub(a,i) 15 integer i 16 real a 17 !$OMP DO REDUCTION(+: a) 18 do j = 1, 10 19 a = a + i + j 20 end do 21 end subroutine sub as_35_1.f(17): エラー #12208: 変数 "A" は REDUCTION 句 (ファイル:as_35_1.f 行:4) で指定されているため、囲まれたコンテキストで SHARED でなければなりません。 |