ユーザ用ツール

サイト用ツール


faq:scs-faq:parallel

プログラムの並列化について

プログラムの並列化とは

並列化には、以下のような方法があります。

  • 自動並列化 コンパイラによって並列化可能な処理を自動的に並列化します。
  • OpenMP 複数のCPUを持つ1台のコンピュータ内で並列化する規格です。
  • MPI 複数のコンピュータを接続した大規模な並列化を行ないます。

並列化

コンパイラが自動的に並列化可能な処理を判断しますので、利用者はこれまでに書いたプログラムをそのままで並列化することが可能です。 ただし、簡単に並列化が可能ですが、並列度の高いプログラムができるとは限りません。 並列度を高くすると、かえって処理時間が長くなる場合もあります。

並列化

OpenMPは、SMP(複数のCPUを持つ1台のコンピュータ)上で並列化を行う規格で、Fortran, C, C++言語のプログラムで利用することができます。 プログラムにOpenMPの並列化指示を入れることで、並列化を行ないます。 OpenMP並列化指示の無いプログラムは、並列化できません。 複数のコンピュータを使った並列化はできません。

OpenMP

MPI(Message Passing Interface)ライブラリは、複数の計算機と通信を行ない、並列処理を行うことができます。 複数の計算ノードを使った大規模な並列化が可能です。 ただし、プログラムの構造や規模によって、多くのCPUを使っても早くなるとは限りません。 MPIを利用するには、プログラムに明示的に並列化指示を入れる必要があります。 MPIの指示が無いプログラムは、並列化できません。

Message_Passing_Interface

プログラムの構造やデータによって、並列処理の効率が違います。

並列化は、複数のCPUで、同時に複数の処理を行うことで、プログラムの実行時間を短するプログラミング手法です。 プログラムが、独立した処理に分割できない場合は、並列化の効率が悪く時間も短縮すす事ができません。

並列度(分割処理の数)を大きくした場合に、「処理を分割し、それぞれのCPU(メモリ)に処理命令とデータを分配し、計算した結果を統合する」といった、計算処理以外の通信部分が大きくなり、処理速度を低下させる場合もありますので、どれくらいの並列度が効率的かを確認し、CPU資源を有効に使うようにしてください。

OpenMPや自動並列化では、計算ノードのCPU(core)数以内(16並列まで)となります。

MPI並列については、largeキューの最大 288並列(16並列 x 18ノード)まで利用可能です。

プログラムによっては、並列実行CPUを多くしても、実行時間がそれほど短くならない事があります。

time コマンドなど、実行時間とCPU時間を計測し、並列化の効果を見ながら適切なCPU数を選択してください。 また、インタラクティブの場合、システムの過負荷や、他の利用者への影響を考慮し、適切なCPU数(8並列まで)を指定してください。

例:time コマンドで計測した 8 並列の実行時間

  real 9.34    ← プログラム実行にかかった時間
  user 69.38   ← CPUがユーザプログラムを処理した時間
  sys 0.15     ← プログラム実行でシステムが使ったCPU時間

  user 69.38 / real 9.34 = 7.43  ← 8 並列の効果があった

並列化プログラムのコンパイル、実行について

分散並列型クラスタシステム(TypeA)・大規模共有メモリ型システム(TypeB)

コンパイル:自動並列化オプション(Intel ifort icc コンパイラ)を付けてコンパイルします。

-parallel オプションの指定により、コンパイラがソースコード内のループに対して、自動並列化の解析を行います。

-par_repot{0|1|2|3} 並列化されたループを表示し、並列化されなかった、あるいは並列化しなかったループについては、その理由をレポートします。末尾の数字が大きいほど詳細な情報をレポートします。

※ 自動並列化は、スレッドによる並列化が行われます。

実行:環境変数 OMP_NUM_THREADS に、実行プロセス数を指定して実行します。

・自動並列化コンパイルの例

   ifort -parallel -par_report3 -o program.omp program.f

・インタラクティブ実行の場合

   setenv OMP_NUM_THREADS 4
   ./program.omp

・PBSバッチジョブ実行の場合 バッチジョブスクリプトに環境変数 OMP_NUM_THREADS の指定を記述します。

 バッチジョブスクリプト(omp-job)の例

   #!/bin/sh
   #PBS -q small -N omp-test -j oe -l select=1:ncpus=8
   #PBS -m abe -M norin@affrc.go.jp
   cd $PBS_O_WORKDIR
   OMP_NUM_THREADS=8
   ./program.omp

 バッチジョブ実行

   qsub omp-job

OpenMPの指示行が書かれたプログラムは、以下のようにコンパイル、実行を行います。

コンパイル:OpemMP並列化オプション(Intel ifort icc コンパイラ)を付けてコンパイルします。

-openmp オプションの指定により、ソースコード内の OpenMP 指示行を有効にします。

-parallel オプションの指定により、コンパイラがソースコード内のループに対して、自動並列化の解析を行います。

自動並列化オプション -parallel を同時に指定することもできます。

-openmp と -parallel を同時に指定した場合、指示行は -openmp により並列化され、指示行のないループについては、-parallel によって自動並列化が試みられます。

実行:環境変数 OMP_NUM_THREADS に、実行プロセス数を指定して実行します。

・OpenMP並列化コンパイルの例

   ifort -openmp -o program.omp program.f

・インタラクティブ実行の場合

   setenv OMP_NUM_THREADS 4
   ./program.omp

・PBSバッチジョブ実行の場合 バッチジョブスクリプトに環境変数 OMP_NUM_THREADS の指定を記述します。

 バッチジョブスクリプト(omp-job)の例

   #!/bin/sh
   #PBS -q small -N omp-test -j oe -l select=1:ncpus=8
   #PBS -m abe -M norin@affrc.go.jp
   cd $PBS_O_WORKDIR
   OMP_NUM_THREADS=8
   ./program.omp

 バッチジョブ実行

   qsub omp-job

MPIの指示行が書かれたプログラムは、以下のようにコンパイル、実行を行います。

コンパイル:Intel ifort icc コンパイラに MPI ライブラリをリンクするコマンドを使用します。

mpiifort, mpiiccコマンドは、MPI ライブラリをリンクします。

実行:実行時は mpirun コマンドを使ってジョブを起動してください。 -np オプションを使って、プロセッサ数を指定します。

・MPI並列化コンパイルの例

   mpiifort -o program.mpi program.f
   
   SGI MPT(Message Passing Toolkit)の場合(2012年システム Type A, B)
   ifort -o program.mpi program.f90 -lmpi

・インタラクティブ実行の場合

   mpirun -np 4 ./program.mpi

・PBSバッチジョブ実行の場合

 バッチジョブスクリプト(mpi-job)の例(2ノード x 16プロセス = 32 並列)

   #!/bin/sh
   #PBS -q large -N mpi-test -j oe -l select=2:ncpus=16:mpiprocs=16
   #PBS -m abe -M norin@affrc.go.jp
   cd $PBS_O_WORKDIR
   _NPROCS=`wc -l < $PBS_NODEFILE`
   mpirun -np $_NPRIOCS ./program.mpi

 SGI MPT(Message Passing Toolkit)の場合(2012年システム Type A, B)  mpirun, mpiexec は、使用しないでください。

   #PBS -q large -N test -j oe -l select=8:ncpus=16:mpiprocs=16 
   mpijob 実行プログラム名 

 ※ “-np プロセス数” のオプションは不要です(select、mpiprocs から自動的に設定します) 8 x 16 = 128 並列

 バッチジョブ実行

   qsub mpi-job

ベクトル型システム(TypeC)(SX-9) ※2017年2月末でサービス終了しました 

コンパイル:SX用クロスコンパイラ(sxf90, sxcc, sxc++)に、自動並列化オプション(-P auto)を付けてコンパイルします。

-P autoオプションの指定により、コンパイラがソースコードに対して、自動並列化の解析を行います。

・自動並列化コンパイルの例

sxf90 -P auto -o program.par program.f90

・実行するには、バッチスクリプトで使用するCPU数を指定します。

バッチジョブスクリプト(test-job.sh)の例

#!/bin/sh
#PBS -q default
#PBS -e test-job.err -o test-job.out
#PBS -l cpunum_job=4,cpunum_prc=4
F_PROGINF=detail
export F_PROGINF
F_RSVTASK=4
export F_RSVTASK
/sx/user1/program.par

バッチジョブスクリプトに実行件を付与

chmod +x test-job.sh

バッチジョブの実行

qsub test-job.sh

コンパイル:SX用クロスコンパイラ(sxf90, sxcc, sxc++)に、OpenMP並列化オプション(-P openmp)を付けてコンパイルします。

-P openmpオプションの指定により、コンパイラがソースコード内のOpenMP指示行を有効にします。

・OpenMPコンパイルの例

sxf90 -P openmp -o program.omp program-omp.f90

・実行するには、バッチスクリプトで使用するCPU数を指定します。

バッチジョブスクリプト(test-job.sh)の例

#!/bin/sh
#PBS -q default
#PBS -e test-job.err -o test-job.out
#PBS -l cpunum_job=4,cpunum_prc=4
F_PROGINF=detail
export F_PROGINF
OMP_NUM_THREDS=4
export OMP_NUM_THREDS
cd /sx/user1/sample/omp
./program.omp > outputfile 2>&1

バッチジョブスクリプトに実行件を付与

chmod +x test-job.sh

バッチジョブの実行

qsub test-job.sh

コンパイル:SX MPI用クロスコンパイラ(sxmpif90, sxmpicc, sxmpic++)を使ってコンパイルします。

SX MPI用コンパイラにより、MPI/SX, MPI2/SXライブラリがリンクされ、ソースコード内のMPI指示行を有効にします。

・MPIコンパイルの例

sxmpif90 -o program.mpi program-mpi.f90

・実行するには、バッチスクリプトで使用するCPU数を指定し、mpirunコマンドを使って実行します。

バッチジョブスクリプト(test-job.sh)の例

#!/bin/sh
#PBS -q default
#PBS -e test-job.err -o test-job.out
#PBS -l cpunum_job=4,cpunum_prc=4
F_PROGINF=detail
export F_PROGINF
mpirun -np 4 /sx/user1/sample/mpi/program.mpi

バッチジョブスクリプトに実行件を付与

chmod +x test-job.sh

バッチジョブの実行

qsub test-job.sh

並列化プログラムの再現性について

浮動小数点の丸め誤差、加算の演算順序によって結果が変わるなどが原因です。

必要とする有効桁数以下を無視するなどの対策が必要です。

Q. OpenMPについて質問です。

OpenMPのホームページ http://www.openmp.org/ から取ってきたサンプルプログラムを使って並列化の実験をしました。

並列なしでシングルプロセッサーでこのプログラムを実行すると2回繰り返しても同じ結果が得られました。

今度は並列4プロセッサーで実行すると2回繰り返して違う結果が得られました。

どうして並列化すると再現性が無くなるのでしょうか.

コンパイラーオプション、実行時の環境変数などはどう設定するのでしょう?

A.

1. 浮動小数点演算について

並列化に関する議論の前に、計算機の浮動小数点演算についてお話いたします。

例:1のプログラムをご覧下さい。

単純な足し算をするプログラムであり、res0とres1は足し合わせる順番が異なるだけです。本来であれば結果は同一になるはずですが、異なっています。

計算機における浮動小数点演算では、丸め誤差が生じるためです。

※この例で使ったプログラムでは、Intel ifort コンパイラの場合、結果が同じ(ただし、表示桁数が少ない)となった。  ここでは、丸め誤差の説明のため、g77 の結果で説明していますが、Intel コンパイラでも丸め誤差は生じます。

問題になるのは、結果の有効桁をどこまで要求するかということです。 (例えばkmオーダの議論で、mmやμmの差は通常考慮しません)

2. 並列化されているプログラムの場合

並列化の最も簡単な例は、並列スレッドで計算した部分和を加算し総和を求めるというものがあります。 www.openmp.orgのmd.fはその典型です。

並列スレッドで部分和を計算し、総和を求めるプログラムでは演算順序がタイミングにより変わります。(基本的に、制御がOSとライブラリに任されているためです。)つまり、上記1.と同様のことが起こっています。厳密な精度を求める場合は、プログラム内で工夫をする、もしくは並列化が不可能な場合もあります。

並列化されたプログラム、商用アプリケーションは多数存在しますが、必要としている有効桁まで結果が合っていればそれ以下は考慮しないことが慣例です。

まず、ご利用の環境で計算結果の差がどの程度のオーダで発生しているかをご確認ください。

入力パラメータが同一にもかかわらず、結果の差が許容範囲内にない場合は(桁が全く異なる等)お知らせください。

例:1
$ cat a.f
implicit none
real*4 a, b, c, d, res0, res1
parameter(a=0.0004,b=1.0000,c=0.0004,d=0.0002)

res0 = (( a + b ) + c ) + d
res1 = (( d + a ) + c ) + b

write(*,*) res0
write(*,*) res1
end 

$ g77 -ffree-form -o a a.f
$ ./a
 1.00099993
 1.00100005

※ Intel ifort や gfortran コンパイラの出力は、res0, res1 が同じ。
   ただし、表示桁数が少ない。

$ ifort -free -o a a.f
$ ./a
   1.001000
   1.001000

$ gfortran -ffree-form -o a a.f
$ ./a
   1.001000
   1.001000
faq/scs-faq/parallel.txt · 最終更新: 2019/02/06 16:33