maxwell方程式の散解法( fdtd法)と 並列シミュ...
TRANSCRIPT
Maxwell方程式の離散解法(FDTD法)と並列シミュレーション
2日目
松本正晴大学院情報理工学系研究科コンピュータ科学専攻
計算科学アライアンスサマースクール2017年8月23日(水)~25日(金)
講義資料について
本スライドのPDFファイルhttp://sudalab.is.s.u-tokyo.ac.jp/~matsumoto/SS2017
講義に関する質問は随時メールをください。
2日目の内容
• FDTD法サンプルプログラムの実行(逐次計算)
• 並列プログラミングの基本的知識
• FDTD法(陽解法)のMPI並列化
サンプルプログラムの実行
サンプルプログラムについて
• C言語版・Fortran90版共通ファイル: 09_fdtd.tar
• tarで展開後、C言語とFortran90のディレクトリが作られる。C/ :C言語用F/ :Fortran90用
• 上記ファイルが置いてある場所/lustre/gi20/c26050
• 以下のディレクトリがある。2DTM_serial/ 2DTE_para/
Reedbush上でサンプルプログラムのコピー
1. ターミナルを起動してReedbushへログイン(-Xオプションを忘れず)$ ssh –X reedbush-u.cc.u-tokyo.ac.jp -l i16xxx
2. cdwコマンドを実行してLustreファイルシステムへ移動$ cdw
3. サンプルプログラムの場所/lustre/gi16/c26050にある
09_fdtd.tarを自分のディレクトリにコピーする$ cp /lustre/gi16/c26050/09_fdtd.tar ./
4. 09_fdtd.tarを展開する$ tar xvf 09_fdtd.tar
Jobスクリプトサンプルの説明job.bash, C言語,Fortran共通
#!/bin/bash
#PBS -q u-debug
#PBS -Wgroup_list=gi16
#PBS -l select=8:mpiprocs=4:ompthreads=8
#PBS -l walltime=00:10:00
cd $PBS_O_WORKDIR
. /etc/profile.d/modules.sh
mpirun ./a.out
リソースグループ名
利用グループ名:gi16
利用ノード数
ノード内MPIプロセス数
実行時間制限:10分
カレントディレクトリ設定、環境変数設定(必ず記入しておく)
上の例では、ジョブを32プロセス8スレッドの256並列(コア)で実行(1ノード当たり4プロセス8スレッドでそれが8ノード分)
1プロセス当たりOpenMP
スレッド数
Reedbush-Uは1ノード当たり36コア。
2DTE_serialプログラムの説明
1コア(逐次)実行版です ジョブスクリプト(job.bash)の記述に注意!
OpenMPによるスレッド並列化も可能です。ジョブスクリプトのompthreadsの値を変更することで、スレッド数の指定ができます。
ジョブスクリプト内に使用スレッド情報を記載#PBS -l select=1:mpiprocs=1:ompthreads=36
2DTE_serialプログラムの説明
int main(int argc,char *argv[]){
const int ntmax=1001,nxmax=201,nymax=201,nabsp=16;
const double CFL=0.95;
double dx[2]={1.0e-2,1.0e-2};
…
ダイポールアンテナの計算条件が設定されている。主なパラメータは以下。
ntmax: 全タイムステップ数nxmax: x方向の格子点数nymax: y方向の格子点数nabsp: PML層の格子点数CFL: クーラン数dx: 空間格子幅[m]
program FDTD_2DTE
use omp_lib
implicit none
integer,parameter::ntmax=1000,nxmax=201,nymax=201,nabsp=16
double precision,parameter::CFL=0.95d0
double precision,parameter::dx(1:2)=1.0d-2
…
C言語
Fortran
Reedbush上でプログラムのコンパイル・実行
1. 2DTE_serial/に移動して、ソースコードをコンパイルする。コンパイル用の実行ファイル(./compile)を用意しているので、それを実行。実行ファイルa.outができたどうか確認後、ジョブを投入$ cd 2DTE_serial/
$ ./compile
$ qsub job.bash
2. 計算が終了後、field/ディレクトリに各タイムステップにおける結果が出力されている。prop00001.dat~prop00100.dat
Reedbush上でプログラムのコンパイル・実行
3. Reedbush上でgnuplotを起動し、計算結果を可視化する。$ gnuplot
gnuplot> set pm3d map
gnuplot> set size ratio 1
gnuplot> set cbr[-1:1]
gnuplot> sp”./field/prop00100.dat”us 1:2:4
4. 2DTE_serial/ディレクトリにあるdata.gpファイルをloadさせるとアニメーションが見れる(はず)。gnuplot> load ”data.gp”
演習課題I
2DTE_serialプログラムの計算モデルを変更して、入射波が単スリットで回折する様子をシミュレーションしてください。
• メインループ中のext_currentルーチンをコメントアウトし、代わりにincidentルーチンのコメントアウトを解除(電流励振による電磁波の誘導から、直接的な電磁波の入射へ切り替え)。
• cond_boundaryルーチンのSlitと書かれている範囲のコメントアウトを解除し、dipole antennaと書かれている範囲をコメントアウト。
• incidentルーチンのomgで入射波の周波数を変更可能(デフォルトは2GHz)。
• 計算結果をgnuplotで可視化(アニメーション)してみよ。
並列化プログラミングの基本的知識
並列プログラミングとは?
逐次(並列化されていない)実行のプログラム(実行時間T)を,p台の計算機を使って,T/pにすること。
素人考えでは自明だが,実際にできるかどうかは,対象処理の内容(アルゴリズム)で大きく難しさが異なる。• アルゴリズム上,絶対に並列化できない部分が存在する場合。• 通信のためのオーバーヘッドが存在する場合。
• 通信立ち上がり時間• データ転送時間
T
T/p
並列と並行 並列(Parallel) 物理的に並列(時間的に独立) ある時間に実行されるものは多数
並行(Concurrent) 論理的に並列(時間的に依存) ある時間に実行されるものは1つ
時分割多重,擬似並列 OSによるプロセス実行スケジューリング(ラウンドロビン方式)
性能評価指標 -台数効果-
速度向上率(台数効果) 0 pPSP STTS
(TS:逐次の実行時間,TP:P台での実行時間)
コンピュータの台数を増やして何倍速くなったか? P台用いてSP = Pのとき,理想的(ideal)な速度向上 P台用いてSP > Pのとき,スーパーリニア・スピードアップ
並列化によりデータアクセスが局所化されて、キャッシュヒット率が向上することによる高速化
並列化効率 0[%]100 PPP EPSE
一般には,速度向上には限界がある。 Saturation,「さちる」→アムダールの法則
Amdahl’s Law(アムダールの法則)
逐次実行時間をTSとする。そのうち,並列化できる割合をαとする。この時,台数効果は以下のように表される。
111
1
1
PT
P
T
TS
SS
SP
上記の式から,例え無限大の数のプロセッサを使っても(P→∞),台数効果は高々1/(1-α)にしかならない。
全体の90%が並列化できたとしても,無限大の数のプロセッサを使っても,1/(1-0.9)=10倍にしかならない!
高性能を達成するためには,少しでも並列化効率を上げる実装を目指すことが非常に重要である。
Amdahl’s Lawの直感例
9/3=3倍
9/2=4.5倍 ≠ 6倍
並列化できない部分(1ブロック) 並列化できる部分(8ブロック)
=88.8%が並列化可能逐次実行
並列実行(4並列)
並列実行(8並列)
Gustafson’s Law(グスタフソンの法則)
並列実行時間をTPとする。そのうち,並列化できない割合をβとする。この時,台数効果は以下のように表される。
1
1
PP
T
TPTS
P
PPP
この式では,Pに比例して速度が向上することになり,アムダールの法則と矛盾するように見える。
アムダールの法則では,作業負荷や問題規模が一定であることを仮定している一方,グスタフソンの法則では,作業負荷や問題規模がプロセッサ数に比例して大きくなり,その負荷増加は逐次処理部分に影響しないことを仮定。
Gustafson’s Lawの直感例
逐次実行
並列化できない部分 並列化できる部分
並列実行(4並列)
並列実行(8並列)
並列度の増加に対して問題規模が比例して増加する結果,計算時間は変わらない。
一方で,解いている問題規模がほぼ比例して増加。
結果論的に,並列実行の問題規模を逐次実行で解く場合を考えると,台数効果が大きく見える。
スケーラビリティ
0
10
20
30
40
50
60
70
0 10 20 30 40 50 60 70
Spe
ed
up
The Number of Processors
Amdahl’s Law(α = 0.9)
Amdahl’s Law (α = 0.5)
Gustafson’s Law(β = 0.1)
Gustafson’s Law(β = 0.5)
並列処理における2つのスケーリング
1.強いスケーリング(Strong Scaling)問題規模を固定したとき,速度向上率SPがPにほぼ比例すること。すなわち並列化効率EPがほぼ1に近い一定値であること。アムダールの法則より,Pが増加すると強いスケーリングは成り立たない。
2.弱いスケーリング(Weak Scaling)並列度Pとともに問題規模を増加させたとき,すなわちプロセッサ当たりの問題規模を固定したとき,SPがPにほぼ比例すること。グスタフソン
の法則の前提のように,問題規模を大きくしても,並列化不可能な計算時間がほぼ一定ならば,弱いスケーリングが成り立つ。
並列計算で計算速度が向上しない原因
(a)並列化できない処理アムダールの法則から明らかなように,並列処理可能部分が大きくなければ計算速度は向上しない。
(b)通信Message Passingなどによってデータを他のプロセッサに送るには時間がかかる。通信中に他の演算を行うことで,通信時間を隠蔽できるが,アルゴリズムによる。通信は逐次処理にはなかったので,確実に並列処理の時間を増加させる。
(c)同期(Synchronization)処理の都合で,プログラムのあるところで全てのプロセッサが待ち合わせることがある。このとき,1つでも遅いプロセッサがあると,他のプロセッサが遊んでしまい,処理の時間が増加する。
(d)負荷の不均衡(Load unbalance)プロセッサごとに割り当てた仕事の負荷が不均衡であると,早く仕事が終わったプロセッサが遊んでしまい,並列処理の障害になる。
(e)アルゴリズムの不適切逐次計算で効率の良いアルゴリズムが並列計算で効率が良いとは限らない。
並列計算機のメモリ型による分類(1/2)
1.共有メモリ型並列コンピュータ(各プロセッサがメモリアドレスを共有)
(a)対称型マルチプロセッサ(SMP)Symmetric Multiprocessor
Shared Memory Processor
PE PE PE PE
Memory
メモリの場所がどのプロセッサからも等距離にある。OpenMP言語を通じて並列化されることが多い。Uniform Memory Access(UMA)とも。
(b)分散共有メモリ型コンピュータ(DSM)Distributed Shared Memory
メモリは物理的に分散しているが,全体として唯一の線形なアドレス空間を共有。Non-Uniform Memory Access(NUMA)とも。
PE
M
PE
M
PE
M
PE
M
Network
並列計算機のメモリ型による分類(2/2)
2.分散メモリ型並列コンピュータ(複数のコンピュータをネットワークで結合)
(a)メッセージパッシングMessage Passing
• プロセッサとメモリがあれば1つのコンピュータと言え,各コンピュータをノードと呼ぶ。
• 分散メモリ型では,各ノードごとにメモリは独立。ノード間はネットワークを経由して通信を行う。
• Message Passing Interface(MPI)が共通の規格として用いられることが多い。
PE
M
PE
M
PE
M
PE
M
Network
(b)ハイブリッド並列処理
現在では,分散メモリ型コンピュータを構成するノードが共有メモリ型コンピュータである場合がほとんど。プロセス並列(MPI) + スレッド並列(OpenMP)のハイブリッドが多い。
Reedbush-U 1ノードの構成
メモリ128GB
Intel Xeon E5-2695
v4 (Broadwell-EP)
18コア
DDR4
DDR4
DDR4
DDR4
QP
I
QP
I
メモリ128GB
Intel Xeon E5-2695
v4 (Broadwell-EP)
18コア
DDR4
DDR4
DDR4
DDR4
76.8GB/s76.8GB/s76.8GB/s
1ノード当たり36コア、256GB
×420ノード
G3 x16
Infin
iband
ED
R H
CA
一般的なスーパーコンピュータでは、ノード単位で計算資源を数えることが多い。
Reedbush-Uの場合、1ノード当たり36コア、256GB(18コア、128GBが2つ)。これが全部で420
ノードある。
ノード内は共有メモリ型計算機なので、OpenMPによる並列化が可能。
ノード間で並列化する際はMPIによるデータ通信が必要(分散メモリ型計算機)。
メモリ型による並列プログラムの種類
マルチプロセス• Message Passing Interface(MPI)• High Performance Fortran(HPF)
自動並列化Fortranコンパイラ ユーザがデータ分割方法を明示的に記述
マルチスレッド• Pthread(POSIXスレッド)• Solaris Thread(Sun Solaris OS用)• NT thread(WinNT系,Win95以降)• Java
言語仕様としてスレッドを規定
• Open Multi-Processing(OpenMP) ユーザが並列化指示行を記述
プロセスとスレッドの違い⇒ メモリを意識するかどうかの違い・分散メモリ間は「プロセス」・共有メモリ内は「スレッド」
マルチプロセスとマルチスレッドは共存可能。⇒MPI/OpenMPハイブリッド並列実行
並列計算機の分類とMPIの関係
MPIは分散メモリ型コンピュータに欠かせない通信ライブラリ• 分散メモリ型コンピュータでは明示的な通信が必要
MPIは共有メモリ型コンピュータでも動作する• 共有メモリ上でプロセス間通信が出来る
MPIを用いたプログラミングモデルは(基本的に)SIMD的• MPIは,(一般的には)プログラムが1つ(=命令と等価)しかないが,データ(配列など)は複数あるため
MPI並列プログラミングのモデル
• SPMD(Single Program Multiple Data stream):1つの共通のプログラムが並列処理開始時に全プロセッサ上で起動。
• MPIによるプログラミングはSPMDが基本。
MPI/OpenMPハイブリッド並列化コード(具体例)
program main
implicit none
include ‘mpif.h’
integer::i,n=100
integer::irank,isize,ierr
call mpi_init(ierr)
call mpi_comm_rank(…,irank,ierr)
call mpi_comm_size(…,isize,ierr)
!$omp parallel do
do i=1,n
<calc.>
enddo
!$omp end parallel do
call mpi_finalize(ierr)
end program main
#include<mpi.h>
int main(int argc, char *argv[]){
int i,n=100;
int irank,isize,ierr;
ierr=MPI_Init(&argc, &argv);
ierr=MPI_Comm_rank(…,&irank);
ierr=MPI_Comm_size(…,&isize);
#pragma omp parallel for
for(i=0;i<n;i++){
<calc.>
}
ierr=MPI_Finalize();
}
C言語 Fortran
MPIで並列化されたコードにOpenMPのディレクティブを追加
FDTD法(陽解法)のMPI並列化
2D-FDTD法(陽解法)のiteration
x
y
Ey
Ex
Hz
2D-FDTD法(陽解法)のiteration
x
y
更新
Ey
Ex
Hz
電場のupdate
2D-FDTD法(陽解法)のiteration(逐次計算)
x
y
Ey
Ex
Hz
更新
電場のupdate
2D-FDTD法(陽解法)のiteration(逐次計算)
x
y
Ey
Ex
Hz
更新
電場のupdate
2D-FDTD法(陽解法)のiteration(逐次計算)
x
y
Ey
Ex
Hz
更新
電場のupdate
2D-FDTD法(陽解法)のiteration(逐次計算)
x
y
Ey
Ex
Hz
更新
磁場のupdate
2D-FDTD法(陽解法)のiteration(逐次計算)
x
y
Ey
Ex
Hz
更新
磁場のupdate
2D-FDTD法(陽解法)のiteration(逐次計算)
x
y
Ey
Ex
Hz
更新
磁場のupdate
2D-FDTD法(陽解法)のiteration(逐次計算)
x
y
Ey
Ex
Hz
更新
磁場のupdate
4並列時の計算領域
x
y
Rank.0 Rank.1
Rank.2 Rank.3
4並列時の計算の流れ
x
y
Rank.0 Rank.1
Rank.2 Rank.3
更新
更新
更新
更新
電場のupdate
4並列時の計算の流れ
x
y
Rank.0 Rank.1
Rank.2 Rank.3
更新
更新
更新
更新
電場のupdate
4並列時の計算の流れ
x
y
Rank.0 Rank.1
Rank.2 Rank.3
更新
更新
更新
更新
電場のupdate
4並列時の計算の流れ
x
y
Rank.0 Rank.1
Rank.2 Rank.3
更新
更新
更新
更新
電場のupdate
4並列時の計算の流れ
x
y
Rank.0 Rank.1
Rank.2 Rank.3
Send Send
1回iterationしたら通信して外点をupdate
Recv
電場のデータ通信
Recv
4並列時の計算の流れ
x
y
Rank.0 Rank.1
Rank.2 Rank.3
磁場のupdate
更新
更新
更新
更新
4並列時の計算の流れ
x
y
Rank.0 Rank.1
Rank.2 Rank.3
磁場のupdate
更新
更新
更新
更新
4並列時の計算の流れ
x
y
Rank.0 Rank.1
Rank.2 Rank.3
磁場のupdate
更新
更新
更新
更新
4並列時の計算の流れ
x
y
Rank.0 Rank.1
Rank.2 Rank.3
磁場のupdate 更新
更新
更新
更新
4並列時の計算の流れ
x
y
Rank.0 Rank.1
Rank.2 Rank.3
SendSend
1回iterationしたら通信して外点をupdate
Recv
Recv
磁場のデータ通信
ステンシル計算の分散並列化
各rankでiterationを1回行ったらMPI通信で外点(Ghost)の値をupdateする。
外点(Ghost)に、隣接するrankの物理量をMPI通信で受信するとともに、逆に内点の端を隣接rankへ送信。
rank0 rank1 rank2 rank3
rank4 rank5 rank6 rank7
rank8 rank9 rank10 rank11
x
y 例えば,x方向に4プロセス,y方向に3プロセスの場合,計12プロセスは左のような形状になる。
構造格子の場合、左のような形状で分散並列化すると、プロセス数の増減が簡単に行える。
MPI通信
MPI通信の流れ
話を簡単にするために、レギュラー格子で説明。
格子点数がx方向にnxmax,y方向にnymaxだけあるとする。
nxmax
nymax
MPI通信の流れ
1. 受信バッファを用意。
上下左右4箇所の隣接プロセスから点線の外点にデータを受信する際に、点線と同じ大きさのバッファを用意して、まずはそこにデータを入れる。rbuf_x1(nymax)
rbuf_y1(nxmax)
rbuf_y2(nxmax)
rbuf_x1(nymax)
MPI通信の流れ
2. 送信バッファを用意。
上下左右4箇所の隣接プロセスへ内点の端の値を送信する際に、送信バッファからデータを送信する。
sbuf_x1(nymax)
sbuf_y1(nxmax)
sbuf_y2(nxmax)
sbuf_x1(nymax)
MPI通信の流れ
MPI_IsendとMPI_Irecvはノンブロッキング通信を行うMPI関数。
隣接プロセスからまだMPI_Isend
が発行されていないので、この時点では受信バッファにデータは来ない。受信待ちの状態。
rbuf_x1(nymax)
rbuf_y1(nxmax)
rbuf_y2(nxmax)
rbuf_x1(nymax)
3. MPI_Irecvを発行。
MPI通信の流れ
4. 送信バッファにデータをパック。
左図の点線に囲まれた格子点上の物理量をsbufにパックする。
sbuf_x1(nymax)
sbuf_y1(nxmax)
sbuf_y2(nxmax)
sbuf_x1(nymax)
5. MPI_Isendを発行。
送信バッファに格納されているデータを送信先プロセスのrbufへ送信。
6. MPI_Waitallで送受信を完了させる。
MPI_Waitallが呼ばれるまでは受信バッファと送信バッファはいじっちゃダメ。
MPI通信の流れ
MPI_Waitallを抜けるとデータの受信が完了しているので、受信バッファの中身を外点にアンパック。
rbuf_x1(nymax)
rbuf_y1(nxmax)
rbuf_y2(nxmax)
rbuf_x1(nymax)
7. 受信バッファの中身を格子点上へアンパック
MPI通信部分のコード例(C言語,1/2)
void data_comm(略){
…
double sbuf_x1[nymax],sbuf_x2[nymax],sbuf_y1[nxmax],sbuf_y2[nxmax];
double sbuf_x1[nymax],sbuf_x2[nymax],sbuf_y1[nxmax],sbuf_y2[nxmax];
MPI_Request ireq[8];
MPI_Status stat[8];
…
// -- recv data --
MPI_Irecv(rbuf_x2,nymax,MPI_DOUBLE,rtbl[px+1][py],1,MPI_COMM_WORLD,&ireq[0]);
MPI_Irecv(rbuf_x1,nymax,MPI_DOUBLE,rtbl[px-1][py],2,MPI_COMM_WORLD,&ireq[1]);
MPI_Irecv(rbuf_y2,nxmax,MPI_DOUBLE,rtbl[px][py+1],3,MPI_COMM_WORLD,&ireq[2]);
MPI_Irecv(rbuf_y1,nxmax,MPI_DOUBLE,rtbl[px][py-1],4,MPI_COMM_WORLD,&ireq[3]);
// -- pack data –
for(ny=1;ny<=nymax;ny++){
sbuf_x1[ny-1]=f[1][ny]; sbuf_x2[ny-1]=f[nxmax][ny];
}
for(nx=1;nx<=nxmax;nx++){
sbuf_y1[nx-1]=f[nx][1]; sbuf_y2[nx-1]=f[nx][nymax];
}
送信バッファと受信バッファの定義
上下左右4箇所の隣接プロセスから来るデータを受信バッファで受け取る
送信バッファにデータをパッキング
MPI通信部分のコード例(C言語,2/2)
// -- send data --
MPI_Isend(sbuf_x1,nymax,MPI_DOUBLE,rtbl[px-1][py],1,MPI_COMM_WORLD,&ireq[4]);
MPI_Isend(sbuf_x2,nymax,MPI_DOUBLE,rtbl[px+1][py],2,MPI_COMM_WORLD,&ireq[5]);
MPI_Isend(sbuf_y1,nxmax,MPI_DOUBLE,rtbl[px][py-1],3,MPI_COMM_WORLD,&ireq[6]);
MPI_Isend(sbuf_y2,nxmax,MPI_DOUBLE,rtbl[px][py+1],4,MPI_COMM_WORLD,&ireq[7]);
// -- wait --
MPI_Waitall(8,ireq,stat);
// -- unpack data --
for(ny=1;ny<=nymax;ny++){
f[0][ny]=rbuf_x1[ny-1]; f[nxmax+1][ny]=rbuf_x2[ny-1];
}
for(nx=1;nx<=nxmax;nx++){
f[nx][0]=rbuf_y1[nx-1]; f[nx][nymax+1]=rbuf_y2[nx-1];
}
上下左右4箇所の隣接プロセスへ送信バッファの内容を送信
MPI_IsendとMPI_Irecvを完了するためのWaitall
Waitallを抜けると、
受信バッファのデータを取り出せる。
subroutine data_comm(略)
…
double precision::sbuf_x1(nymax),sbuf_x2(nymax),sbuf_y1(nxmax),sbuf_y2(nxmax)
double precision::rbuf_x1(nymax),rbuf_x2(nymax),rbuf_y1(nxmax),rbuf_y2(nxmax)
integer::ireq(1:8),stat(mpi_status_size,1:8)
…
! -- recv data –
call mpi_irecv(rbuf_x2,nymax,mpi_double_precision,rtbl(px+1,py),1,mpi_comm_world,ireq(1),ierr)
call mpi_irecv(rbuf_x1,nymax,mpi_double_precision,rtbl(px-1,py),2,mpi_comm_world,ireq(2),ierr)
call mpi_irecv(rbuf_y2,nxmax,mpi_double_precision,rtbl(px,py+1),3,mpi_comm_world,ireq(3),ierr)
call mpi_irecv(rbuf_y1,nxmax,mpi_double_precision,rtbl(px,py-1),4,mpi_comm_world,ireq(4),ierr)
! -- pack data --
do ny=1,nymax
sbuf_x1(ny)=f(1,ny); sbuf_x2(ny)=f(nxmax,ny)
enddo
do nx=1,nxmax
sbuf_y1(nx)=f(nx,1); sbuf_y2(nx)=f(nx,nymax)
enddo
MPI通信部分のコード例(Fortran90,1/2)
送信バッファと受信バッファの定義
上下左右4箇所の隣接プロセスから来るデータを受信バッファで受け取る
送信バッファにデータをパッキング
! -- send data --
call mpi_isend(sbuf_x1,nymax,mpi_double_precision,rtbl(px-1,py),1,mpi_comm_world,ireq(5),ierr)
call mpi_isend(sbuf_x2,nymax,mpi_double_precision,rtbl(px+1,py),2,mpi_comm_world,ireq(6),ierr)
call mpi_isend(sbuf_y1,nxmax,mpi_double_precision,rtbl(px,py-1),3,mpi_comm_world,ireq(7),ierr)
call mpi_isend(sbuf_y2,nxmax,mpi_double_precision,rtbl(px,py+1),4,mpi_comm_world,ireq(8),ierr)
! -- wait --
call mpi_waitall(8,ireq(1:8),stat(:,1:8),ierr)
! -- unpack data --
do ny=1,nymax
f(0,ny)=rbuf_x1(ny); f(nxmax+1,ny)=rbuf_x2(ny)
enddo
do nx=1,nxmax
f(nx,0)=rbuf_y1(nx); f(nx,nymax+1)=rbuf_y2(nx)
enddo
return
end subroutine data_comm
MPI通信部分のコード例(Fortran90,2/2)上下左右4箇所の隣接プロセスへ送信バッファの内容を送信
MPI_IsendとMPI_Irecvを完了するためのWaitall
Waitallを抜けると、
受信バッファのデータを取り出せる。
rank_tableの作成
MPI_Isend,MPI_Irecvを行う際に、どのプロセス(rank)とデータの送受信を行うか、4つめの引数で与える必要がある。
x方向のプロセス数をpxmax,y方向のプロセス数をpymaxとすると、全プロセス数iproc=pxmax*pymaxとなる。
自分のrankIDと自分がpx,pyで言うとどこにあるかを対応付ける。 rankIDとpx,pyを対応付けるテーブルを作る。
rank0 rank1 rank2 rank3
rank4 rank5 rank6 rank7
rank8 rank9 rank10 rank11
x
y
例えば、左の場合はpxmax=4,pymax=3 自分のrankIDが6だった場合、px=3,py=2という値を覚えておく。
rtbl(px,py)=rankIDというテーブルを作っておけば、自分(px,py)の左隣はrtbl(px-1,py),上隣はrtbl(px,py+1)でrankIDがわかる。
存在しないpx,pyでは、rankIDとしてMPI_PROC_NULLを設定しておくとMPI_IsendとMPI_Irecvを無視してくれるから便利。