*並列実行プログラムの最適化 [#lc0ec8aa]
#contents
** 自動並列化 [#a8f1a4a1]
parallelオプションの指定により、コンパイラがソースコード内の全ループに対して、並列化が可能か否かの解析を行います。~
~
- -parallel
--ソースファイル内の全てのループを解析し、安全に並列化できるループがあれば並列バイナリを生成します。
- -par-threshold[n]
-- 自動並列化の性能向上の見込みのしきい値(確信度)を設定します。([n]は0~100まで) デフォルトは100。
-- n が 0 のとき、ループは、計算量にかかわらず、並列化可能ならば常に自動並列化されます。
n が 100 のとき、並列化が可能でかつ並列化による性能向上が見込まれる場合のみ自動並列化されます。
- -qopt-report[=n] -qopt-report-phase=par (-qopt-reportは省略可)
~
自動並列化の診断情報を出力します。
--n=1:並列化されたループを出力します。
--n=2:レベル 1 の詳細に加えて、並列化されなかったループとその簡単な理由を出力します。
--n=3:レベル 2 の詳細に加えて、private、shared、reduction として分類されているメモリー位置を出力します。
--n=4:レベル3と同じ
--n=5:レベル 4 の詳細に加えて、並列化を妨げている依存関係を出力します。
--デフォルトはn=2。
~
(*)コンパイラのバージョン15以降、診断情報を出力する -opt-reportオプションが -qopt-reportに変わりました。-opt-reportもお使い頂けますがワーニングメッセージが出力されます。
~
診断情報は、「バイナリ名.optrpt」ファイルに出力されます。
~
-例)~
 $ cat mxm.f
 
   1       SUBROUTINEMXM(A,B,C,L,M,N,N1)
   2       DIMENSION A(M,L),B(N1,M),C(N1,L)
   3       DO  K =1,L
   4         DO  I =1,N
   5           C(I,K)=0.
   6         ENDDO
   7       ENDDO
   8       DO K =1,L
   9         DO I =1,N
  10           DO J =1,M
  11             C(I,K)=C(I,K)+B(I,J)*A(J,K)
  12           ENDDO
  13         ENDDO
  14       ENDDO
  15       RETURN
  16       END
 
 $ ifort  -c  -O3  -xAVX -parallel  -qopt-report -qopt-report-phase=par mxm.f
 ifort: remark #10397: optimization reports are generated in *.optrpt files in the output location
 
 mxm.optrptファイルに診断情報が出力されます。
 8行目のループが並列化されたことがわかります。
 
 $ ifort  -c  -O3  -xAVX  -parallel  -par-threshold0 -qopt-report -qopt-report-phase=par  mxm.f
 ifort: remark #10397: optimization reports are generated in *.optrpt files in the output location
 
 mxm.optrptファイルに診断情報が出力されます。
 3行目と8行目のループが並列化されたことがわかります。

- -par-threshold100(デフォルト)の場合は、初期化ループ(3行目)には充分な処理量が無く、性能向上が確実ではないと判断し、並列化しません。
- -par-threshold0の場合は、性能向上の見込み値には関係なく、安全に並列化できるループは全て並列化します。
** OpenMP [#bff9624d]
qopenmpオプションの指定により、ソースコード内のOpenMP指示行を有効にします。
~
(*)コンパイラのバージョン15以降、OpenMP指示行を有効にするオプションが -openmp から -qopenmp へ変わりました。-openmp もお使い頂けますがワーニングメッセージが出力されます。
~
診断情報は、「バイナリ名.optrpt」ファイルに出力されます。


- -qopenmp
--OpenMP指示行を有効にします。
- -qopt-report[=n] -qopt-report-phase=openmp (-qopt-reportは省略可)
--OpenMP並列化の診断情報を出力します。
--n=1:正常に並列化されたループ、領域、セクション、タスクを出力します。
--n=2:1の情報に加えて、正常に処理された MASTER 構造、SINGLE 構造、CRITICAL 構造、ORDERED 構造、ATOMIC 宣言子などを示す診断メッセージを出力します。
--デフォルトはn=2。
~
(*)コンパイラのバージョン15以降、診断情報を出力する -opt-reportオプションが -qopt-reportに変わりました。-opt-reportもお使い頂けますがワーニングメッセージが出力されます。
-例)~
 $ cat mxm2.f
 
   1       SUBROUTINE MXM (A,B,C,L,M,N,N1)
   2       DIMENSION A(M,L),B(N1,M),C(N1,L)
   3 !$OMP PARALLEL DO
   4       DO  K =1,L
   5         DO  I =1,N
   6           C(I,K)=0.
   7         ENDDO
   8       ENDDO
   9 !$OMP PARALLAL DO
  10       DO K =1,L
  11         DO I =1,N
  12           DO J =1,M
  13             C(I,K)=C(I,K)+B(I,J)*A(J,K)
  14           ENDDO
  15         ENDDO
  16       ENDDO
  17       RETURN
  18       END
 
 $ ifort -c  -O3 -xAVX  -qopenmp  -qopt-report -qopt-report-phase=openmp mxm2.f
 ifort: remark #10397: optimization reports are generated in *.optrpt files in the output location
 
 mxm2.optrptファイルに診断情報が出力されます。

- -qopenmpと-parallelを同時に指定したとき
~
コンパイラは全ループに対して並列化可能か否かの解析を行い、可能であれば並列化します。~
OpenMP指示行が指定されたループは、-qopenmpによって並列化さます。~
-例)
 $ cat mxm3.f
 
   1       SUBROUTINE MXM (A,B,C,L,M,N,N1)
   2       DIMENSION A(M,L),B(N1,M),C(N1,L)
   3       DO  K =1,L
   4         DO  I =1,N
   5           C(I,K)=0.
   6         ENDDO
   7       ENDDO
   8 !$OMP PARALLEL DO
   9       DO K =1,L
  10         DO I =1,N
  11           DO J =1,M
  12             C(I,K)=C(I,K)+B(I,J)*A(J,K)
  13           ENDDO
  14         ENDDO
  15       ENDDO
  16       RETURN
  17       END
 
 $ ifort -c -O3 -xAVX -parallel  -par-threshold0  -qopt-report \
 -qopt-report-phase=par -qopenmp  -qopt-report-phase=openmp  mxm3.f
 ifort: remark #10397: optimization reports are generated in *.optrpt files in the output location
 
 mxm3.optrptファイルに診断情報が出力されます。
 
 3行目と9行目のループが自動並列化され、8行目のOpenMP指示行が有効になります。
 9行目のループは自動並列化とOpenMPの両方から並列化されますが、環境変数
 OMP_NESTED=falseがデフォルトで設定されているため、環境変数OMP_NUM_THREADS
で指定したスレッド数で実行されます。
OMP_NESTED=falseは入れ子になった並列処理を無効にします。
 
OMP_NUM_THREADSについては、[[「自動並列化/OpenMPプログラムの実行スレッド数」>#lafe8ab3]]を参照下さい。


**自動並列化/OpenMPプログラムの実行スレッド数 [#lafe8ab3]
-環境変数OMP_NUM_THREADSに実行スレッド数を指定します。
 $ export OMP_NUM_THREADS=4
 
ジョブスクリプト、ジョブ投入方法については、[[「利用方法」>利用方法#w8339976]]を参照下さい。
 
**自動並列化/OpenMPによる並列プログラムのデータ分散~ [#ke6c26fe]
-EICシステムでは、「ファーストタッチ」を採用しています。データは、最初にそのデータにタッチ(書き込みまたは読み込み)したプロセスのローカルメモリに配置されるというものです。もし、配列(配列Aとする)を初期化しているループが並列化されていないと1CPUコア実行であるために、配列Aはある1つのノードに配置されます。配列Aを処理する並列実行領域では、複数のプロセッサが1つのノードへアクセスすることになります。このような状況ではプログラムの性能向上は望めません。可能なかぎり初期化ループも並列化してください。~
例)
    real*8 A(n), B(n), C(n), D(n)
 
     do i=1, n
         A(i) = 0.
         B(i) = i/2
         C(i) = i/3
         D(i) = i/7
     enddo
 !$omp parallel do
     do i=1, n
         A(i) = B(i) + C(i) + D(i)
     enddo
~
#ref(first_touch1.JPG,left,nowrap,80%)
~
     real*8 A(n), B(n), C(n), D(n)
 !$omp parallel do
     do i=1, n
         A(i) = 0.
         B(i) = i/2
         C(i) = i/3
         D(i) = i/7
     enddo
 !$omp parallel do
     do i=1, n
         A(i) = B(i) + C(i) + D(i)
     enddo
#ref(first_touch2.JPG,left,nowrap,80%)

** スレッドのスタックサイズ [#zd84c197]
-各ユーザが使用可能なスタックサイズの上限を6GBに設定しています(メモリ使用量の上限ではありません)。関数や手続きの中で宣言されるローカル変数がスタック領域にアロケーションされます。上限を越えてスタックに領域を確保しようとすると、AddressErrorまたは、SegmentationFaultでプログラムは終了します。この場合、データを静的にアロケーションするために、COMMONブロックでデータ(配列)を宣言して下さい。
-環境変数KMP_STACKSIZEには自動並列化/OpneMPプログラムのスレーブスレッドが使用できるスタック領域の最大容量を指定します。スレーブスレッドのプライベート変数が対象です。共有変数、マスタースレッドのプライベート変数は対象ではありません。デフォルトでは2GBが設定されています。
-単位として、k(キロバイト)、m(メガバイト)、g(ギガバイト)、t(テラバイト)が使えます。
-シリアル実行で実行できたプログラムが、自動並列化/OpenMP並列化したときにエラー終了してしまったというような場合にはスレッドのスタック領域が不足している可能性がありますので、KMP_STACKSIZEを増やしてください。
~
例)
 $export KMP_STACKSIZE=4g

**MPI [#t936c9e4]
コマンド行の末尾にMPIライブラリのリンクを指示してください。~
 
例)
 $ ifort  -O3  -xAVX  ex_mpi.f -lmpi
実行時にはmpiexec_mptコマンドを使ってジョブを起動してください。
-MPIのデータ型~
FortranプログラムでのMPIのデータ型とそれに対応するFortranでのデータ型は以下の通りです。~
|~MPIのデータ型|~Fortranのデータ型|~bit|
|MPI_DATATYPE_NULL|対応する型はありません|―|
|MPI_INTEGER|INTEGER|32|
|MPI_REAL|REAL|32|
|MPI_DOUBLE_PRECISION|DOUBLE_PRECISION|64|
|MPI_COMPLEX|COMPLEX|64|
|MPI_DOUBLE_COMPLEX|DOUBLE_COMPLEX|128|
|MPI_LOGICAL|LOGICAL|4|
|MPI_CHARACTER|CHARACTER|1|
|MPI_INTEGER1|INTEGER(KIND=1)|8|
|MPI_INTEGER2|INTEGER(KIND=2)|16|
|MPI_INTEGER4|INTEGER(KIND=4)|32|
|MPI_INTEGER8|INTEGER(KIND=8)|64|
|MPI_REAL2|REAL(KIND=2)|16|
|MPI_REAL8|REAL(KIND=8)|64|
|MPI_REAL16|REAL(KIND=16)|128|
|MPI_BYTE|BYTE|1|
~
CプログラムでのMPIのデータ型とそれに対応するCでのデータの型は以下の通りです。~
|~MPIのデータ型|~Cのデータ型|~bit|
|MPI_CHAR|char|8|
|MPI_SHORT|short|16|
|MPI_INT|int|32|
|MPI_LONG|long|64|
|MPI_UNSIGHNED_CHAR|unsighned char|8|
|MPI_UNSIGHNED_SHORT|unsighned short|16|
|MPI_UNSIGHNED|unsighned int|32|
|MPI_UNSIGHNED_LONG	|unsighned long|64|
|MPI_FLOAT|float|32|
|MPI_DOUBLE|double|64|
|MPI_LONG_DOUBLE|long_double|128|
|MPI_BYTE|対応する型はありません|1|
|MPI_PACKED|対応する型はありません||
**ハイブリッド [#ka4a7d3a]
並列化を有効にするコンパイルオプションを指定して、リンク時にMPIライブラリをリンクします。~
~
例)
 $  ifort  -O3  -xAVX -qopenmp  -qopt-report -qopt-report-phase=openmp \
 hybrid.f  -lmpi
実行時にはmpiexec_mptコマンドを使ってジョブを起動してください。~
ジョブスクリプト、ジョブ投入方法は、[[「利用方法」>利用方法#w8339976]]」を参照ください。
**性能解析ツール [#m5de4d99]
-''実行時間のかかっている箇所を探す''~
~
--自動並列化/OpenMPプログラム
~
psrunコマンドにオプション -p を指定してください。 -p オプションの指定で子スレッドまでの解析が可能です。psrunコマンドと共に自動並列化/OpenMPプログラムを実行すると、OMP_NUM_THREADSで指定した数のスレッドの他に1つの管理スレッドが起動されます。そのため解析結果ファイルも実行スレッド数+1個作られます。~
行毎の解析を行いたいときには、コンパイル時に -g オプションを指定してください。~
~
例)
 $ export  OMP_NUM_THREADS=4
 $ dplace  -x5  psrun  -p  ./a.out   <---dplaceコマンドのオプションは -x5 です。
~
実行終了後、解析結果ファイルがカレントディレクトリに作られます。
 $ ls*.xml
 a.out.3.4032.eich1.xml
 a.out.2.4032.eich1.xml
 a.out.4.4032.eich1.xml
 a.out.1.4032.eich1.xml
 a.out.0.4032.eich1.xml
(解析結果ファイル名はバイナリ名.スレッド番号.PID.ホスト名.xmlです。)~
psprocessコマンドで整形してください。~
 $ psprocess  a.out.0.4032.eich1.xml
~
(*)解析結果ファイルを複数指定して、全スレッドの解析結果を表示するということはできません。~
 $ psprocess  a.out.0.4032.eich1.xml  a.out.2.4032.xml  a.out.3.4032.xml  <---不可
~
実行例)~
行毎の解析を行うために -g オプションを指定し、並列化のために自動並列化オプション指定します。
 $ ifort  -g  -O3 -xSSE4.2 -parallel  -par-threshold0  -par-report1  test.f
 $ dplace  -x5  psrun  -p  ./a.out
 $ ls *.xml
 a.out.4.16904.eich1.xml
 a.out.3.16904.eich1.xml
 a.out.2.16904.eich1.xml
 a.out.1.16904.eich1.xml
 a.out.0.16904.eich1.xml
 $ psprocess  a.out.0.16904.eich1.xml
 
 Samples      Self%    Total%  Function  
      7922   87.82%   87.82%  L_jacobi__202__par_region0_2_137  
      1043   11.56%   99.38%  __intel_new_memcpy
        34    0.38%   99.76%  main$himenobmtxp_m_omp_$BLK
         6    0.07%   99.82%  _intel_fast_memcpy.J
 
 Samples     Self%  Total%   Function:File:Line
      1260  13.97% 13.97%   L_jacobi__202__par_:himeno_omp.f:215
      1162  12.88% 26.85%   L_jacobi__202__par_:himeno_omp.f:219
      1043  11.56% 38.41%   __intel_new_memcpy:??:0
       993  11.01% 49.42%   L_jacobi__202__par:himeno_omp.f:216
--MPIプログラム~
MPIプログラムを解析するときはpsrunコマンドに-fオプションを指定してください。
dplaceのオプションは、-s2となります。~
~
--ハイブリッドプログラム~
ハイブリッドプログラムを解析するときは、psrunコマンドに-fオプションと-pオプションを指定してください。~
実行時にはomplaceコマンドを指定して下さい。omplaceコマンドのオプションは、psrunを指定しないときと変わりません。
~
~
-''MPInside(MPIの通信時間を測る)''~
~
MPInsideを使ってMPI関数の実行回数や、通信時間を知ることができます。~
コマンド名はMPInsideです。
~
例)~
 $ mpiexec_mpt -np 4 omplace MPInside a.out
実行が終わるとカレントディレクトリに "mpinside_stats"というファイルが作成されます。


トップ   新規 一覧 単語検索 最終更新   ヘルプ