MPI「超」入門（FORTRAN編）
東京大学情報基盤センター
C言語編は以下
http://www.cspp.cc.u-tokyo.ac.jp/ohshima/seminars/t2k201111/	

（MPIによる並列アプリケーション開発入門 ２）	1

Fundamental MPI

概要	
• MPIとは• MPIの基礎：Hello World• 全体データと局所データ• グループ通信（Collective Communication）	
• 1対1通信（Point-to-Point Communication）	

2

Fundamental MPI

MPIとは （1/2）	
• Message Passing Interface• 分散メモリ間のメッセージ通信APIの「規格」– プログラム，ライブラリ，そのものではない• http://phase.hpcc.jp/phase/mpi-j/ml/mpi-j-html/contents.html• 歴史– 1992– 1994– 1997

MPIフォーラム
MPI-1規格
MPI-2規格（拡張版），現在はMPI-3が検討されている• 実装– mpich アルゴンヌ国立研究所 http://www-unix.mcs.anl.gov/mpi/– Open MPI オープンソース http://www.open-mpi.org/about/members/– 各ベンダー– C/C++,FOTRAN,Java ; Unix，Linuｘ，Windows，Mac OS

3

Fundamental MPI

MPIとは （2/2）	
• 現状ではmpich（フリー）が広く使用されている– 部分的に「MPI-2」規格をサポート– 2005年11月から「MPICH2」に移行• Open MPIの使用も増えている– モジュール構造のため改造が容易• MPIが普及した理由– MPIフォーラムによる規格統一• どんな計算機でも動く• FORTRAN，Cからサブルーチンとして呼び出すことが可能– mpichの存在• フリー，あらゆるアーキテクチュアをサポート• 同様の試みとしてPVM（Parallel Virtual Machine）があったが，こちらはそれほど広がらず	4

Fundamental MPI

参考文献	
• P.Pacheco 「MPI並列プログラミング」，培風館，2001（原著1997）• W.Gropp他「Using MPI second edition」，MIT Press, 1999.
• M.J.Quinn「Parallel Programming in C with MPI and OpenMP」,
McGrawhill, 2003.
• W.Gropp他「MPI：The Complete Reference Vol.I, II」，MIT Press,
1998.
• http://www-unix.mcs.anl.gov/mpi/www/– API（Application Interface）の説明	

5

Fundamental MPI

MPIを学ぶにあたって（1/2）	
• 文法– 「MPI-1」の基本的な機能（10程度）について習熟する• MPI-2では色々と便利な機能があるが・・・– あとは自分に必要な機能について調べる，あるいは知っている人，
知っていそうな人に尋ねる• 実習の重要性– プログラミング– その前にまず実行してみること• SPMD/SIMDのオペレーションに慣れること・・・「つかむ」こと– Single Program/Instruction Multiple Data
– 基本的に各プロセスは「同じことをやる」が「データが違う」• 大規模なデータを分割し，各部分について各プロセス（プロセッサ）が計算する– 全体データと局所データ，全体番号と局所番号	6

Fundamental MPI

PE: Processing Element
プロセッサ，領域，プロセス	

SPMD

mpirun -np M <Program>

この絵が理解できればMPIは9割方理解できたことになる。
コンピュータサイエンスの学科でもこれを上手に教えるのは難しいらしい。	

PE #0

PE #1

PE #2

PE #M-1

Program

Program

Program

ProgramData #0

Data #1

Data #2

Data #M-1

各プロセスは「同じことをやる」が「データが違う」	
大規模なデータを分割し，各部分について各プロセス（プロセッサ）が計算する	
通信以外は，単体CPUのときと同じ，というのが理想	Fundamental MPI

用 語	

7

• プロセッサ，コア– ハードウェアとしての各演算装置。シングルコアではプロセッサ＝コア• プロセス– MPI計算のための実行単位，ハードウェア的な「コア」とほぼ同義。
– しかし1つの「プロセッサ・コア」で複数の「プロセス」を起動する場合もある（効率的ではないが）。

• PE（Processing Element）– 本来，「プロセッサ」の意味なのであるが，本講義では「プロセス」の意
味で使う場合も多い。次項の「領域」とほぼ同義でも使用。
• マルチコアの場合は：「コア＝PE」という意味で使うことが多い。

• 領域– 「プロセス」とほぼ同じ意味であるが，SPMDの「MD」のそれぞれ一つ，「各データ」の意味合いが強い。しばしば「PE」と同義で使用。

• MPIのプロセス番号（PE番号，領域番号）は0から開始– したがって8プロセス（PE，領域）ある場合は番号は0～7

8Fundamental MPI

PE: Processing Element
プロセッサ，領域，プロセス	

SPMD

mpirun -np M <Program>

この絵が理解できればMPIは9割方理解できたことになる。
コンピュータサイエンスの学科でもこれを上手に教えるのは難しいらしい。	

PE #0

PE #1

PE #2

PE #M-1

Program

Program

Program

ProgramData #0

Data #1

Data #2

Data #M-1

各プロセスは「同じことをやる」が「データが違う」	
大規模なデータを分割し，各部分について各プロセス（プロセッサ）が計算する	
通信以外は，単体CPUのときと同じ，というのが理想	9Fundamental MPI

MPIを学ぶにあたって（2/2）	
• 繰り返すが，決して難しいものではない。
• 以上のようなこともあって，文法を教える授業は2~3回程度で充分と考えている（今回はもっと短い：正味90分くらいか）。
• とにかくSPMDの考え方を掴むこと !

10Fundamental MPI

内 容	• 環境管理• グループ通信	
– Collective Communication• 1対1通信	– Point-to-Point Communication

Fundamental MPI• MPIとは• MPIの基礎：Hello World• 全体データと局所データ• グループ通信（Collective Communication）	
• 1対1通信（Point-to-Point Communication）	

11

12

Fundamental MPI

まずはプログラムの例	
hello.f

implicit REAL*8 (A-H,O-Z)
include 'mpif.h‘
integer :: PETOT, my_rank, ierrcall MPI_INIT(ierr)
call MPI_COMM_SIZE (MPI_COMM_WORLD, PETOT, ierr )call MPI_COMM_RANK (MPI_COMM_WORLD, my_rank, ierr )write (*,'(a,2i8)') 'Hello World FORTRAN', my_rank, PETOT
call MPI_FINALIZE (ierr)
stop
end

hello.c#include "mpi.h"#include <stdio.h>
int main(int argc, char **argv){
int n, myid, numprocs, i;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
printf ("Hello World %d\n", myid);
MPI_Finalize();}Fundamental MPI

hello.f/c をコンパイルしてみよう！	
>$ cd <$FVM>/S1>$ mpicc –Os -noparallel hello.c>$ mpif90 –Oss –noparallel hello.f

FORTRAN$> mpif90 –Oss -noparallel hello.f“mpif90”：	

	
 FORTRAN90＋MPIによってプログラムをコンパイルする際に		
 必要な，コンパイラ，ライブラリ等がバインドされている 	

C言語	
$> mpicc –Os -noparallel hello.c“mpicc”：	

	
 C＋MPIによってプログラムをコンパイルする際に		
 必要な，コンパイラ，ライブラリ等がバインドされている	
13

Fundamental MPI

ジョブ実行	
• 実行方法	– 基本的にバッチジョブのみ	
– インタラクティヴの実行は「基本的に」できません• 実行手順	– ジョブスクリプトを書きます	– ジョブを投入します	– ジョブの状態を確認します	– 結果を確認します	• その他	
– 実行時には1ノード（16コア）が占有されます	– 他のユーザーのジョブに使われることはありません	
14

Fundamental MPI

ジョブスクリプト	
• <$FVM>/S1/hello.sh• スケジューラへの指令 ＋ シェルスクリプト	
#@$-r hello#@$-q lecture#@$-N 1#@$-J T4#@$-e err#@$-o hello.lst#@$-lM 28GB#@$-lT 00:05:00#@$

実行ジョブ名（qstatで表示、指定無し時はファイル名）	
 実行キュー名：必須	 使用ノード数：必須	
 ノードあたりMPIプロセス数（T1~T16）：推奨	 標準エラー出力ファイル名	
 
標準出力ファイル名	
 1ノードあたりメモリ使用量（固定）	
 実行時間（上限15分，この場合は5分）：推奨	
 ※1T（イチ、ティー）ではなく、lT（エル、ティー）	

cd $PBS_O_WORKDIR
mpirun numactl --localalloc ./a.out

実行ディレクトリ移動	
 mpirun15

Fundamental MPI

ジョブスクリプト（詳細）	
#@$-r hello#@$-q lecture#@$-N 1#@$-J T4#@$-e err#@$-o hello.lst#@$-lM 28GB#@$-lT 00:05:00#@$

実行ジョブ名（qstatで表示）	
 実行キュー名	 使用ノード数	 ノードあたりMPIプロセス数（T1~T16）	
 標準エラー出力ファイル名	
 
標準出力ファイル名	
 1ノードあたりメモリ使用量（固定）	
 実行時間（上限15分，この場合は5分）	
 cd $PBS_O_WORKDIR
mpirun numactl --localalloc ./a.out

実行ディレクトリ移動	
 mpirun• mpirun –np XXは不要：N×Jがプロセス数• 普通は「mpirun –np 4 a.out」のように走らせる	16

Fundamental MPI

ジョブ投入	>$ cd <$FVM>/S1>$ qsub hello.sh>$ cat hello.lst
Hello World 0
Hello World 3
Hello World 2
Hello World 1

17

Fundamental MPI

利用可能なキュー	#@$-r hello#@$-q lecture#@$-N 1#@$-J T4

実行ジョブ名（qstatで表示、指定無し時はファイル名）	
 実行キュー名	 使用ノード数	 ノードあたりMPIプロセス数（T1~T16）	
 • 以下の2種類のキューを利用可能	
– lecture• 4ノード（64コア），15分，アカウント有効期間中利用可能	• 1回に1ジョブのみ実行可能（全教育ユーザーで共有）	

– tutorial• 4ノード（64コア），15分，講義時間のみ	
• lectureよりは多くのジョブを投入可能（混み具合による）	
18

Fundamental MPI

ジョブ投入，確認等	
••••

ジョブの投入ジョブの確認キューの状態の確認
ジョブの取り消し・強制終了

	
qsub スクリプト名	
	
qstat
	
qstat –b	
qdel ジョブID[t15026@ha8000-3 S1]$ qstat -b
2008/08/24 (Sun) 12:59:33:
BATCH QUEUES on HA8000 cluster
NQS schedule stop time : 2008/08/29 (Fri) 9:00:00 (Remain: 116h 0m 27s)
QUEUE NAMESTATUSTOTALRUNNING RUNLIMIT QUEUED HELDIN-TRANSIT
lectureAVAILBL001000lecture5
STOPPED004
0
00[t15026@ha8000-3 S1]$ qsub go.sh
Request 79880.batch1 submitted to queue: lecture.
[t15026@ha8000-3 S1]$ qstat
2008/08/24 (Sun) 12:59:43:
REQUESTS on HA8000 cluster
NQS schedule stop time : 2008/08/29 (Fri) 9:00:00 (Remain: 116h 0m 17s)
REQUEST
NAME
OWNER
QUEUE
PRI NICE
CPU
MEM
STATE
79880.batch1
S1-3
t15026lecture00unlimit
28GB QUEUED[t15026@ha8000-3 S1]$ qdel 79880
deleting request 79880.batch1.
[t15026@ha8000-3 S1]$ qstat
2008/08/24 (Sun) 12:59:51:
REQUESTS on HA8000 cluster
NQS schedule stop time : 2008/08/29 (Fri) 9:00:00 (Remain: 116h 0m 9s)
REQUEST
NAME
OWNER
QUEUE
PRI NICE
CPU
MEM
STATE
No requests.
19Fundamental MPI

結果確認	• ジョブが終了するとメールがきます	– ジョブスクリプトに –mu オプションを書けば任意のメールアドレスに送信できます	– ~/.forward を設定しておけばオプションを書かなくても自分のメールアドレスに送信（転送）できます	• 結果の確認	
– 標準出力と標準エラー出力が指定のファイルに書き込まれる– 指定しなかった場合は• 標準出力• 標準エラー出力

ジョブ名.oジョブID
ジョブ名.eジョブID	

20

Fundamental MPINQSに関連するTIPS	• NQSジョブが実行開始したとき、およびNQSジョブが終了したとき、
電子メールが送られてくる• このメールを読むときは以下のようにする> mail• なお、コマンドは以下のとおりです– 番号： メールの表示
Ex. >2– h： メール一覧表示– s： ファイルへ保存
Ex. > s file
– d： メールの削除
Ex. > d 1
– x： コマンドの終了（何もしない）– q： コマンドの終了（mboxへ保存）• 他のメールアドレスに飛ばしたい場合は、.forwardファイル中にアド
レスを記述
21

22

Fundamental MPI

環境管理ルーチン＋必須項目	
implicit REAL*8 (A-H,O-Z)
include 'mpif.h‘
integer :: PETOT, my_rank, ierrcall MPI_INIT(ierr)
call MPI_COMM_SIZE (MPI_COMM_WORLD, PETOT, ierr )call MPI_COMM_RANK (MPI_COMM_WORLD, my_rank, ierr )write (*,'(a,2i8)') 'Hello World FORTRAN', my_rank, PETOT
call MPI_FINALIZE (ierr)
stop
end

環境変数デフォルト値
FORTRAN90ではuse mpi可MPI_Init
初期化

MPI_Comm_size
プロセス数取得
mpirun -np XX <prog>#include "mpi.h"#include <stdio.h>
int main(int argc, char **argv){
int n, myid, numprocs, i;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
printf ("Hello World %d\n", myid);
MPI_Finalize();}‘mpif.h’, “mpi.h”

MPI_Comm_rank
プロセスID取得
自分のプロセス番号（0から開始）

MPI_Finalize

MPIプロセス終了	

23

Fundamental MPI

FORTRANとCの違い	
• 基本的にインタフェースはほとんど同じ– Cの場合，「MPI_Comm_size」のように「MPI」は大文字，「MPI_」のあとの最初の文字は大文字，以下小文字• FORTRANはエラーコード（ierr）の戻り値を引数の最後に指定する必要がある。
• 最初に呼ぶ「MPI_INIT」だけは違う– call MPI_INIT (ierr)– MPI_Init (int *argc, char ***argv)

24

Fundamental MPI

何をやっているのか ?
implicit REAL*8 (A-H,O-Z)
include 'mpif.h‘
integer :: PETOT, my_rank, ierrcall MPI_INIT(ierr)
call MPI_COMM_SIZE (MPI_COMM_WORLD, PETOT, ierr )call MPI_COMM_RANK (MPI_COMM_WORLD, my_rank, ierr )write (*,'(a,2i8)') 'Hello World FORTRAN', my_rank, PETOT
call MPI_FINALIZE (ierr)stop
end#@$-r hello#@$-q lecture#@$-N 1#@$-J T4#@$-e err#@$-o hello.lst#@$-lM 28GB#@$-lT 00:05:00#@$

実行ジョブ名（qstatで表示）

実行キュー名
使用ノード数
ノードあたりMPIプロセス数（T1~T16）• mpirun -np 4 <prog> により4つのプロセスが立ち上がる（今の場合はT4）。
– 同じプログラムが4つ流れる。
– データの値（my_rank）を書き出す。

標準エラー出力ファイル名
標準出力ファイル名

1ノードあたりメモリ使用量（固定）
実行時間（上限15分，この場合は5分）

cd $PBS_O_WORKDIR
mpirun numactl --localalloc ./a.out•

実行ディレクトリ移動
mpirun••4つのプロセスは同じことをやっているが，データとして取得したプロセスID（my_rank）は異なる。
結果として各プロセスは異なった出力をやっていることになる。
まさにSPMD

25

Fundamental MPI

mpi.h，mpif.h
implicit REAL*8 (A-H,O-Z)
include 'mpif.h‘
integer :: PETOT, my_rank, ierrcall MPI_INIT(ierr)
call MPI_COMM_SIZE (MPI_COMM_WORLD, PETOT, ierr )call MPI_COMM_RANK (MPI_COMM_WORLD, my_rank, ierr )write (*,'(a,2i8)') 'Hello World FORTRAN', my_rank, PETOT
call MPI_FINALIZE (ierr)stop
end#include "mpi.h"#include <stdio.h>
int main(int argc, char **argv){
int n, myid, numprocs, i;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
printf ("Hello World %d\n", myid);
MPI_Finalize();}•

MPIに関連した様々なパラメータおよび初期値を記述。
• 変数名は「MPI_」で始まっている。
• ここで定められている変数は，MPIサ
ブルーチンの引数として使用する以外は陽に値を変更してはいけない。
• ユーザーは「MPI_」で始まる変数を独自に設定しないのが無難。	

26

Fundamental MPI

MPI_Init••

MPIを起動する。他のMPIサブルーチンより前にコールする必要がある（必須）
全実行文の前に置くことを勧める。

• call MPI_INIT (ierr)– ierr

整数

O

完了コード

implicit REAL*8 (A-H,O-Z)
include 'mpif.h‘
integer :: PETOT, my_rank, ierrcall MPI_INIT(ierr)
call MPI_COMM_SIZE (MPI_COMM_WORLD, PETOT, ierr )call MPI_COMM_RANK (MPI_COMM_WORLD, my_rank, ierr )write (*,'(a,2i8)') 'Hello World FORTRAN', my_rank, PETOT
call MPI_FINALIZE (ierr)
stop
end

27

Fundamental MPI

MPI_Finalize•

MPIを終了する。他の全てのMPIサブルーチンより後にコールする必要がある（必須）。
• 全実行文の後に置くことを勧める• これを忘れると大変なことになる。
– 終わったはずなのに終わっていない・・・• call MPI_FINALIZE (ierr)– ierr

整数

O

完了コード

implicit REAL*8 (A-H,O-Z)
include 'mpif.h‘
integer :: PETOT, my_rank, ierrcall MPI_INIT(ierr)
call MPI_COMM_SIZE (MPI_COMM_WORLD, PETOT, ierr )call MPI_COMM_RANK (MPI_COMM_WORLD, my_rank, ierr )write (*,'(a,2i8)') 'Hello World FORTRAN', my_rank, PETOT
call MPI_FINALIZE (ierr)
stop
end

28

Fundamental MPI

MPI_Comm_size•

コミュニケーター 「comm」で指定されたグループに含まれるプロセス数の合計が「size」にもどる。必須では無いが，利用することが多い。

• call MPI_COMM_SIZE (comm, size, ierr)– comm– size– ierr

整数
整数
整数

I
O
O

コミュニケータを指定する
comm.で指定されたグループ内に含まれるプロセス数の合計
完了コード

implicit REAL*8 (A-H,O-Z)
include 'mpif.h‘
integer :: PETOT, my_rank, ierrcall MPI_INIT(ierr)
call MPI_COMM_SIZE (MPI_COMM_WORLD, PETOT, ierr )call MPI_COMM_RANK (MPI_COMM_WORLD, my_rank, ierr )write (*,'(a,2i8)') 'Hello World FORTRAN', my_rank, PETOT
call MPI_FINALIZE (ierr)
stop
end

29

Fundamental MPI

コミュニケータとは ?
MPI_COMM_SIZE (MPI_COMM_WORLD, PETOT, ierr)• 通信を実施するためのプロセスのグループを示す。
• MPIにおいて，通信を実施する単位として必ず指定する必要がある。
• mpirunで起動した全プロセスは，デフォルトで「MPI_COMM_WORLD」というコミュニケータで表されるグ
ループに属する。
• 複数のコミュニケータを使用し，異なったプロセス数を割り当てることによって，複雑な処理を実施することも可能。
– 例えば計算用グループ，可視化用グループ• この授業では「MPI_COMM_WORLD」のみでOK。	

30

Fundamental MPI

コミュニケータの概念
あるプロセスが複数のコミュニケータグループに属しても良い	
MPI_COMM_WORLD
COMM_MANTLE

COMM_CRUST		

COMM_VIS		31

Fundamental MPI

地盤・石油タンク連成シミュレーション	

動 画	

32

Fundamental MPI対象とするアプリケーション	• 地盤・石油タンク振動– 地盤⇒タンクへの「一方向」連成– 地盤表層の変位 ⇒ タンク底面の強制変位として与える• このアプリケーションに対して，連成シミュレーションのためのフレームワークを開発，実装• 1タンク＝1PE：シリアル計算	

Deformation of surface
will be given as
boundary conditions
at bottom of tanks.

33

Fundamental MPI

地盤，タンクモデル	
• 地盤モデル：FORTRAN– 並列FEM，三次元弾性動解析• 前進オイラー陽解法，EBE– 各要素は一辺2mの立方体– 240m×240m×100m• タンクモデル：C– シリアルFEM（EP），三次元弾性動解析• 後退オイラー陰解法，スカイライン法• シェル要素＋ポテンシャル流（非粘性）– 直径：42.7m，高さ：24.9m，厚さ：20mm，液面：12.45m，スロッシング周期：7.6sec.
– 周方向80分割，高さ方向：0.6m幅– 60m間隔で4×4に配置• 合計自由度数：2,918,169

34

Fundamental MPI

3種類のコミュニケータの生成	
meshGLOBAL%MPI_COMM

basement#2

basememt#034

basement#3

basement
#1

tank
#6

tank
#7

tank
#8

tank
#3

tank
#4

tank
#5

tank
#0

tank
#1

tank
#2

meshBASE%MPI_COMM

meshTANK%MPI_COMM

meshGLOBAL%my_rank= 0~3

meshGLOBAL%my_rank= 4~12

meshBASE%my_rank= 0~3

meshTANK%my_rank= 0~ 8

meshTANK%my_rank= -1

meshBASE%my_rank

= -1

35

Fundamental MPI

MPI_Comm_rank•

コミュニケーター 「comm」で指定されたグループ内におけるプロセスIDが「rank」にもどる。必須では無いが，利用することが多い。
– プロセスIDのことを「rank（ランク）」と呼ぶことも多い。

• MPI_COMM_RANK (comm, rank, ierr)– comm– rank整数
整数

IO– ierr

整数

O

コミュニケータを指定する
comm.で指定されたグループにおけるプロセスID
0から始まる（最大はPETOT-1）
完了コード

implicit REAL*8 (A-H,O-Z)
include 'mpif.h‘
integer :: PETOT, my_rank, ierrcall MPI_INIT(ierr)
call MPI_COMM_SIZE (MPI_COMM_WORLD, PETOT, ierr )call MPI_COMM_RANK (MPI_COMM_WORLD, my_rank, ierr )write (*,'(a,2i8)') 'Hello World FORTRAN', my_rank, PETOT
call MPI_FINALIZE (ierr)
stop
end

36

Fundamental MPIMPI_Abort•

MPIプロセスを異常終了する。

• call MPI_ABORT (comm, errcode, ierr)– comm– errcode– ierr

整数
整数
整数

I
O
O

コミュニケータを指定する
エラーコード完了コード

37

Fundamental MPI

MPI_Wtime•

時間計測用の関数：精度はいまいち良くない（短い時間の場合）• time= MPI_WTIME ()– timeR8

O

過去のある時間からの経過時間（秒数）	

…
real(kind=8):: Stime, Etime
Stime= MPI_WTIME ()do i= 1, 100000000
a= 1.d0
enddo
Etime= MPI_WTIME ()write (*,'(i5,1pe16.6)') my_rank, Etime-Stime

38

Fundamental MPI

MPI_Wtime の例	$> cd <$FVM>/S1$> mpicc -O3 time.c$> mpif90 -O3 time.f$> 実行（4プロセス） go4.sh	
0
3
2
1
プロセス
番号	

1.113281E+00
1.113281E+00
1.117188E+00
1.117188E+00
計算時間

39

Fundamental MPI

MPI_Wtick••

MPI_Wtimeでの時間計測精度
ハードウェア，コンパイラによって異なる• time= MPI_Wtick ()O

時間計測精度（単位：秒）	

implicit REAL*8 (A-H,O-Z)
include 'mpif.h'

double Time;– timeR8…
TM= MPI_WTICK ()write (*,*) TM…

…
Time = MPI_Wtick();
printf("%5d%16.6E\n", MyRank, Time);…

Fundamental MPI

MPI_Wtick の例	$> cd <$FVM>/S1$> mpicc -O3 wtick.c$> mpif90 -O3 wtick.f$> （実行：1プロセス） go4.sh	

40

41

Fundamental MPI

MPI_Barrier	•

コミュニケーター 「comm」で指定されたグループに含まれるプロセスの同期をとる。コミュニケータ「comm」内の全てのプロセスがこのサブルーチンを通らない限
り，次のステップには進まない。
• 主としてデバッグ用に使う。オーバーヘッドが大きいので，実用計算には使わない方が無難。
• call MPI_BARRIER (comm, ierr)– comm– ierr

整数
整数

I
O

コミュニケータを指定する
完了コード

Fundamental MPI• MPIとは• MPIの基礎：Hello World• 全体データと局所データ• グループ通信（Collective Communication）	
• 1対1通信（Point-to-Point Communication）	

42

Fundamental MPI

データ構造とアルゴリズム	
• コンピュータ上で計算を行うプログラムはデータ構造とアルゴリズムから構成される。	
• 両者は非常に密接な関係にあり，あるアルゴリズムを実現するためには，それに適したデータ構造が必要である。	
– 極論を言えば「データ構造＝アルゴリズム」と言っても良い。	
– もちろん「そうではない」と主張する人もいるが，科学技術計算に関する限り，「データ構造＝アルゴリズム」と言える。	

• 並列計算を始めるにあたって，基本的なアルゴリズムに適したデータ構造を定める必要がある。	
43

Fundamental MPI

SPMD：Single Program Multiple Data
• 一言で「並列計算」と言っても色々なものがあり，基本的なアルゴリズムも様々。
• 共通して言えることは，SPMD（Single Program Multiple
Data）	
• なるべく単体CPUのときと同じようにできることが理想	
– 通信が必要な部分とそうでない部分を明確にする必要があり。	
	

44

45

Fundamental MPISPMDに適したデータ構造とは ?

PE #0

PE #1

PE #2

PE #3

Program

Program

Program

ProgramData #0

Data #1

Data #2

Data #3

46

Fundamental MPISPMDに適したデータ構造（1/2）	
• 大規模なデータ領域を分割して，各プロセッサ，プロセスで計算するのがSPMDの基本的な考え方• 例えば長さNg(=20)のベクトルVGに対して以下のような計算を考えてみよう：	
integer, parameter :: NG= 20
real(kind=8), dimension(20) :: VG
do i= 1, NG
VG(i)= 2.0 * VG(i)enddo• これを4つのプロセッサで分担して計算するとすれば，
20/4=5 ずつ記憶し，処理すればよい。	

47

Fundamental MPISPMDに適したデータ構造（2/2）	
• すなわち，こんな感じ：	
integer, parameter :: NL= 5
real(kind=8), dimension(5) :: VL
do i= 1, NL
VL(i)= 2.0 * VL(i)enddo• このようにすれば「一種類の」プログラム（Single Program）
で並列計算を実施できる。
– 各プロセスにおいて，「VL」の中身が違う：Multiple Data
– 可能な限り計算を「VL」のみで実施することが，並列性能の高い計算へつながる。	
– 単体CPUの場合ともほとんど変わらない。	

48

Fundamental MPI

全体データと局所データ	
• VG– 領域全体– 1番から20番までの「全体番号」を持つ「全体データ（Global Data）」• VL– 各プロセス（PE，プロセッサ，領域）– 1番から5番までの「局所番号」を持つ「局所データ（Local Data）」– できるだけ局所データを有効に利用することで，高い並列性能が得られる。

Fundamental MPI

局所データの考え方	
「全体データ」VGの：	
	• 1～5番成分が0番PE• 6~10番成分が1番PE• 11～15番が2番PE• 16～20番が3番PEのそれぞれ，「局所データ」VLの1番～5番成分となる（局所番号が1番～5番となる）。	

VG( 1)
VG( 2)
VG( 3)
VG( 4)VG( 5)VG( 6)VG( 7)VG( 8)VG( 9)
VG(10)
VG(11)
VG(12)
VG(13)
VG(14)
VG(15)
VG(16)
VG(17)
VG(18)
VG(19)
VG(20)

PE#0

VL(1)
VL(2)
VL(3)
VL(4)
VL(5)

PE#1

VL(1)
VL(2)
VL(3)
VL(4)
VL(5)

PE#2

VL(1)
VL(2)
VL(3)
VL(4)
VL(5)

PE#3

VL(1)
VL(2)
VL(3)
VL(4)
VL(5)

49

Fundamental MPI

全体データと局所データ	
• VG– 領域全体	– 1番から20番までの「全体番号」を持つ「全体データ（Global Data）」	

• VL– 各プロセッサ	
– 1番から5番までの「局所番号」を持つ「局所データ（Local Data）」	

• この講義で常に注意してほしいこと	
– VG（全体データ）からVL（局所データ）をどのように生成するか。	
– VGからVL，VLからVGへデータの中身をどのようにマッピングするか。	
– VLがプロセスごとに独立して計算できない場合はどうするか。	
– できる限り「局所性」を高めた処理を実施する⇒高い並列性能	
• そのための「データ構造」，「アルゴリズム」	
50

Fundamental MPI• MPIとは• MPIの基礎：Hello World• 全体データと局所データ• グループ通信（Collective Communication）	
• 1対1通信（Point-to-Point Communication）	

51

52

Fundamental MPI

グループ通信とは	• コミュニケータで指定されるグループ全体に関わる通信。
• 例– 制御データの送信– 最大値，最小値の判定– 総和の計算– ベクトルの内積の計算– 密行列の転置	53

Fundamental MPI

グループ通信の例（1/4）	

P#0 A0 B0 C0 D0
P#1

P#0 A0 B0 C0 D0BroadcastP#1 A0 B0 C0 D0

P#2

P#2 A0 B0 C0 D0

P#3

P#3 A0 B0 C0 D0

P#0 A0 B0 C0 D0

P#0 A0

P#1
P#2
P#3

Scatter
GatherP#1 B0P#2 C0P#3 D054

Fundamental MPI

グループ通信の例（2/4）	

P#0 A0P#1 B0P#0 A0 B0 C0 D0
All gather

P#1 A0 B0 C0 D0P#2 C0P#2 A0 B0 C0 D0P#3 D0P#3 A0 B0 C0 D0

P#0 A0 A1 A2 A3

P#0 A0 B0 C0 D0P#1 B0 B1 B2 B3All-to-All

P#1 A1 B1 C1 D1P#2 C0 C1 C2 C3P#2 A2 B2 C2 D2P#3 D0 D1 D2 D3P#3 A3 B3 C3 D3

55

Fundamental MPI

グループ通信の例（3/4）	

P#0 A0 B0 C0 D0P#1 A1 B1 C1 D1P#0 op.A0-A3 op.B0-B3 op.C0-C3 op.D0-D3
Reduce

P#1

P#2 A2 B2 C2 D2

P#2

P#3 A3 B3 C3 D3

P#3

P#0 A0 B0 C0 D0

P#0 op.A0-A3 op.B0-B3 op.C0-C3 op.D0-D3

P#1 A1 B1 C1 D1All reduceP#1 op.A0-A3 op.B0-B3 op.C0-C3 op.D0-D3

P#2 A2 B2 C2 D2P#2 op.A0-A3 op.B0-B3 op.C0-C3 op.D0-D3

P#3 A3 B3 C3 D3P#3 op.A0-A3 op.B0-B3 op.C0-C3 op.D0-D3

56

Fundamental MPI

グループ通信の例（4/4）	

P#0 A0 B0 C0 D0P#1 A1 B1 C1 D1P#0 op.A0-A3
Reduce scatterP#1 op.B0-B3

P#2 A2 B2 C2 D2P#2 op.C0-C3

P#3 A3 B3 C3 D3P#3 op.D0-D3

57

Fundamental MPI

グループ通信による計算例	
• ベクトルの内積	• Scatter/Gather• 分散ファイルの読み込み

58

Fundamental MPI

全体データと局所データ	
• 大規模な全体データ（global data）を局所データ（local
data）に分割して，SPMDによる並列計算を実施する場合のデータ構造について考える。

59

領域分割	
 
• 1GB程度のPC → 106メッシュが限界：FEM– 1000km×1000km×1000kmの領域（西南日本）を1kmメッシュで切ると109メッシュになる	• 大規模データ → 領域分割，局所データ並列処理	
• 全体系計算→ 領域間の通信が必要	

領域分割	
 
大規模
データ	

局所
局所
データ	
  データ	
 局所
局所
データ	  データ	
 

局所
データ	
 

局所
データ	
 
通信

局所
データ	
 

局所
データ	
 

	

Fundamental MPI

60

局所データ構造	 • 対象とする計算（のアルゴリズム）に適した局所データ構造を定めることが重要– アルゴリズム＝データ構造• この講義の主たる目的の一つと言ってよい	

Fundamental MPI

61

Fundamental MPI

全体データと局所データ	
• 大規模な全体データ（global data）を局所データ（local
data）に分割して，SPMDによる並列計算を実施する場合のデータ構造について考える。
• 下記のような長さ20のベクトル，VECpとVECsの内積計算
を4つのプロセッサ，プロセスで並列に実施することを考える。	
VECp( 1)=
( 2)=
( 3)=
…
(18)=
(19)=
(20)=

2
2
2
2
2
2

VECs( 1)=
( 2)=
( 3)=
…
(18)=
(19)=
(20)=

3
3
3
3
3
3

62

Fundamental MPI<$FVM>/S1/dot.f, dot.c
implicit REAL*8 (A-H,O-Z)real(kind=8),dimension(20):: &VECp, VECs
do i= 1, 20
VECp(i)= 2.0d0
VECs(i)= 3.0d0
enddo#include <stdio.h>
int main(){
int i;
double VECp[20], VECs[20]double sum;for(i=0;i<20;i++){VECp[i]= 2.0;
VECs[i]= 3.0;}

sum= 0.d0
do ii= 1, 20
sum= sum + VECp(ii)*VECs(ii)
enddo
stop
end

}

sum = 0.0;
for(i=0;i<20;i++){sum += VECp[i] * VECs[i];}return 0;

63Fundamental MPI<$FVM>/S1/dot.f, dot.cの実行	>$ cd <$FVM>/S1>$ cc -O3 dot.c>$ f90 –O3 dot.f>$ ./a.out

…

1
2
3

2.
2.
2.

3.
3.
3.

18
19
20

2.
2.
2.

3.
3.
3.

dot product
	

120.

64

Fundamental MPI

MPI_Reduce•

P#0 A0 B0 C0 D0P#1 A1 B1 C1 D1P#0 op.A0-A3 op.B0-B3 op.C0-C3 op.D0-D3
Reduce

P#1

P#2 A2 B2 C2 D2

P#2

P#3 A3 B3 C3 D3

P#3

コミュニケーター 「comm」内の，各プロセスの送信バッファ「sendbuf」について，
演算「op」を実施し，その結果を1つの受信プロセス「root」の受信バッファ「recbuf」に格納する。
– 総和，積，最大，最小 他• call MPI_REDUCE(sendbuf,recvbuf,count,datatype,op,root,comm,ierr)
任意
任意

I
O

– count
整数– datatype 整数

II– sendbuf– recvbuf

FORTRANC– op

送信バッファの先頭アドレス，
受信バッファの先頭アドレス，
タイプは「datatype」により決定
メッセージのサイズ
メッセージのデータタイプ

MPI_INTEGER, MPI_REAL, MPI_DOUBLE_PRECISION, MPI_CHARACTER etc.
MPI_INT, MPI_FLOAT, MPI_DOUBLE, MPI_CHAR etc

整数

I

計算の種類

MPI_MAX, MPI_MIN, MPI_SUM, MPI_PROD, MPI_LAND, MPI_BAND etc

ユーザーによる定義も可能： MPI_OP_CREATE– root
整数I
受信元プロセスのID（ランク）– comm
整数
I
コミュニケータを指定する– ierr
整数
O
完了コード

65

Fundamental MPI

送信バッファと受信バッファ	• MPIでは「送信バッファ」，「受信バッファ」という変数がしばしば登場する。
• 送信バッファと受信バッファは必ずしも異なった名称の配列である必要はないが，必ずアドレスが異なっていなければならない。

66

Fundamental MPI

MPI_Reduceの例（1/2）	
call MPI_REDUCE(sendbuf,recvbuf,count,datatype,op,root,comm,ierr)real(kind=8):: X0, X1
call MPI_REDUCE(X0, X1, 1, MPI_DOUBLE_PRECISION, MPI_MAX, 0, <comm>, ierr)real(kind=8):: X0(4), XMAX(4)call MPI_REDUCE(X0, XMAX, 4, MPI_DOUBLE_PRECISION, MPI_MAX, 0, <comm>, ierr)

各プロセスにおける，X0(i)の最大値が0番プロセスのXMAX(i)に入る（i=1~4）	

67

Fundamental MPI

MPI_Reduceの例（2/2）	
call MPI_REDUCE(sendbuf,recvbuf,count,datatype,op,root,comm,ierr)real(kind=8):: X0, XSUM
call MPI_REDUCE(X0, XSUM, 1, MPI_DOUBLE_PRECISION, MPI_SUM, 0, <comm>, ierr)

各プロセスにおける，X0の総和が0番PEのXSUMに入る。	
real(kind=8):: X0(4)call MPI_REDUCE(X0(1), X0(3), 2, MPI_DOUBLE_PRECISION, MPI_SUM, 0, <comm>, ierr)

各プロセスにおける，・ X0（1）の総和が0番プロセスのX0（3）に入る。
・ X0（2）の総和が0番プロセスのX0（4）に入る。	

68

Fundamental MPI
P#0 A0 B0 C0 D0

MPI_Bcast•

P#1

P#0 A0 B0 C0 D0BroadcastP#1 A0 B0 C0 D0

P#2

P#2 A0 B0 C0 D0

P#3

P#3 A0 B0 C0 D0

コミュニケーター 「comm」内の一つの送信元プロセス「root」のバッファ「buffer」
から，その他全てのプロセスのバッファ「buffer」にメッセージを送信。

• call MPI_BCAST (buffer,count,datatype,root,comm,ierr)– buffer

任意I/O– count

整数I

バッファの先頭アドレス，
タイプは「datatype」により決定
メッセージのサイズ– datatype 整数

I

メッセージのデータタイプ

FORTRANC– root– comm– ierr

MPI_INTEGER, MPI_REAL, MPI_DOUBLE_PRECISION, MPI_CHARACTER etc.
MPI_INT, MPI_FLOAT, MPI_DOUBLE, MPI_CHAR etc.

整数
整数
整数

I
I
O

送信元プロセスのID（ランク）
コミュニケータを指定する
完了コード	

69

Fundamental MPIMPI_Allreduce••

P#0 A0 B0 C0 D0P#1 A1 B1 C1 D1P#0 op.A0-A3 op.B0-B3 op.C0-C3 op.D0-D3
All reduceP#1 op.A0-A3 op.B0-B3 op.C0-C3 op.D0-D3

P#2 A2 B2 C2 D2P#2 op.A0-A3 op.B0-B3 op.C0-C3 op.D0-D3

P#3 A3 B3 C3 D3P#3 op.A0-A3 op.B0-B3 op.C0-C3 op.D0-D3

MPI_REDUCE + MPI_BCAST
総和，最大値を計算したら，各プロセスで利用したい場合が多い• call MPI_ALLREDUCE(sendbuf,recvbuf,count,datatype,op,comm,ierr)
任意
任意

I
O

count
整数
datatype 整数
op
整数
comm
整数
ierr
整数

I
I
I
I
O

– sendbuf– recvbuf
–
–
–
–
–

送信バッファの先頭アドレス，
受信バッファの先頭アドレス，
タイプは「datatype」により決定
メッセージのサイズ
メッセージのデータタイプ
計算の種類
コミュニケータを指定する
完了コード

70

MPI_Reduce/Allreduceの “op”
call MPI_REDUCE(sendbuf,recvbuf,count,datatype,op,root,comm,ierr)• MPI_MAX，MPI_MIN• MPI_SUM，MPI_PROD• MPI_LAND

最大値，最小値	
総和，積	
論理AND

real(kind=8):: X0, X1
call MPI_REDUCE(X0, X1, 1, MPI_DOUBLE_PRECISION, MPI_MAX, 0, <comm>, ierr)real(kind=8):: X0(4), XMAX(4)call MPI_REDUCE(X0, XMAX, 4, MPI_DOUBLE_PRECISION, MPI_MAX, 0, <comm>, ierr)Fundamental MPI

71

Fundamental MPI

局所データの考え方（1/2）	
• 長さ20のベクトルを，4つに分割する• 各プロセスで長さ5のベクトル（1~5）	
VECp( 1)=
( 2)=
( 3)=
…
(18)=
(19)=
(20)=

VECs( 1)=
( 2)=
( 3)=
…
(18)=
(19)=
(20)=

2
2
2
2
2
2

3
3
3
3
3
3

72

Fundamental MPI

局所データの考え方（2/2）	
• もとのベクトルの1～5番成分が0番PE，6~10番成分が1番PE，11～15
番が2番PE，16～20番が3番PEのそれぞれ1番～5番成分となる（局所
番号が1番～5番となる）。	
VECp( 1)~VECp( 5)VECs( 1)~VECs( 5)VECp( 6)~VECp(10)VECs( 6)~VECs(10)VECp(11)~VECp(15)VECs(11)~VECs(15)VECp(16)~VECp(20)VECs(16)~VECs(20)PE#0

VECp(1)= 2(2)= 2(3)= 2(4)= 2(5)= 2

VECs(1)= 3(2)= 3(3)= 3(4)= 3(5)= 3

PE#1

VECp(1)= 2(2)= 2(3)= 2(4)= 2(5)= 2

VECs(1)= 3(2)= 3(3)= 3(4)= 3(5)= 3

PE#2

VECp(1)= 2(2)= 2(3)= 2(4)= 2(5)= 2

VECs(1)= 3(2)= 3(3)= 3(4)= 3(5)= 3

PE#3

VECp(1)= 2(2)= 2(3)= 2(4)= 2(5)= 2

VECs(1)= 3(2)= 3(3)= 3(4)= 3(5)= 3

73とは言え・・・	 • 全体を分割して，1から番
号をふり直すだけ・・・とい
うのはいかにも簡単である。	
• もちろんこれだけでは済まない。済まない例については後で紹介する。	

VG( 1)
VG( 2)
VG( 3)
VG( 4)VG( 5)VG( 6)VG( 7)VG( 8)VG( 9)
VG(10)
VG(11)
VG(12)
VG(13)
VG(14)
VG(15)
VG(16)
VG(17)
VG(18)
VG(19)
VG(20)

PE#0

VL(1)
VL(2)
VL(3)
VL(4)
VL(5)

PE#1

VL(1)
VL(2)
VL(3)
VL(4)
VL(5)

PE#2

VL(1)
VL(2)
VL(3)
VL(4)
VL(5)

PE#3

VL(1)
VL(2)
VL(3)
VL(4)
VL(5)

Fundamental MPI

74

Fundamental MPI

内積の並列計算例（１/2）	
<$FVM>/S1/allreduce.f
implicit REAL*8 (A-H,O-Z)
include 'mpif.h'
integer :: PETOT, my_rank, ierrreal(kind=8), dimension(5) :: VECp,

VECs

call MPI_INIT(ierr)
call MPI_COMM_SIZE (MPI_COMM_WORLD, PETOT, ierr )call MPI_COMM_RANK (MPI_COMM_WORLD, my_rank, ierr )sumA= 0.d0
sumR= 0.d0
do i= 1, 5
VECp(i)= 2.d0
VECs(i)= 3.d0
enddo

各ベクトルを各プロセスで独立に生成する	

sum0= 0.d0
do i= 1, 5
sum0= sum0 + VECp(i) * VECs(i)enddo
if (my_rank.eq.0) thenwrite (*,'(a)') '(my_rank, sumALLREDUCE, sumREDUCE)‘
endif

75

Fundamental MPI

内積の並列計算例（2/2）	
<$FVM>/S1/allreduce.f
call MPI_REDUCE (sum0, sumR, 1, MPI_DOUBLE_PRECISION, MPI_SUM, 0, &MPI_COMM_WORLD, ierr)call MPI_ALLREDUCE (sum0, sumA, 1, MPI_DOUBLE_PRECISION, MPI_SUM, &MPI_COMM_WORLD, ierr)

内積の計算
各プロセスで計算した結果「sum0」の総和をとる
sumR には，PE#0の場合にのみ計算結果が入る。
sumA には，MPI_Allreduceによって全プロセスに計算結果が入る。	
call MPI_BCAST (sumR, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, &ierr)

MPI_BCASTによって，PE#0以外の場合にも sumR に計算結果が入る。	

76

Fundamental MPI<$FVM>/S1/allreduce.f の実行例	
$> mpicc –Os-noparallel allreduce.c$> mpif90 –Oss -noparallel allreduce.f$> （実行：4プロセス） go4.sh	(my_rank, sumALLREDUCE,sumREDUCE)
before BCAST
01.200000E+02
after BCAST
01.200000E+02

1.200000E+02
1.200000E+02

before BCAST
after BCAST

1
11.200000E+02
1.200000E+02

0.000000E+00
1.200000E+02

before BCAST
after BCAST

33

1.200000E+02
1.200000E+02

0.000000E+00
1.200000E+02

before BCAST
after BCAST

221.200000E+02
1.200000E+02

0.000000E+00
1.200000E+02

77

Fundamental MPI

グループ通信による計算例	
• ベクトルの内積• Scatter/Gather• 分散ファイルの読み込み

78

Fundamental MPI

全体データと局所データ（1/3）	
• ある実数ベクトルVECgの各成分に実数αを加えるという，以下のような簡単な計算を，「並列化」することを考えてみよう:

do i= 1, NG
VECg(i)= VECg(i) + ALPHA
enddo	for (i=0; i<NG; i++) {VECg[i] = VECg[i] + ALPHA;}	79

Fundamental MPI

全体データと局所データ（2/3）	
• 簡単のために，– NG=32– ALPHA=1000.
– MPIプロセス数=4• ベクトルVECgとして以下のような32個の成分を持つベクトルを仮定する（<$FVM>/S1/a1x.all）：	
(101.0,  103.0,  105.0,  106.0,  109.0,  111.0,  121.0,  151.0,  201.0,  203.0,  205.0,  206.0,  209.0,  211.0,  221.0,  251.0,  301.0,  303.0,  305.0,  306.0,  309.0,  311.0,  321.0,  351.0,    401.0,  403.0,  405.0,  406.0,  409.0,  411.0,  421.0,  451.0)  80

Fundamental MPI

全体データと局所データ（3/3）	
•

計算手順
①

長さ32のベクトルVECgをあるプロセス（例えば0番）で読み込む。
– 全体データ②

4つのプロセスへ均等に（長さ8ずつ）割り振る。
– 局所データ，局所番号③④•

各プロセスでベクトル（長さ8）の各成分にALPHAを加える。
各プロセスの結果を再び長さ32のベクトルにまとめる。

もちろんこの程度の規模であれば1プロセッサで計算できるのである
が・・・	

81

Fundamental MPI

Scatter/Gatherの計算 （1/8）
長さ32のベクトルVECgをあるプロセス（例えば0番）で読み込む。	
• プロセス0番から「全体データ」を読み込む
include   'mpif.h'  integer,   parameter  ::  NG=  32  real(kind=8),  dimension(NG)::  VECg    call  MPI_INIT  (ierr)  call  MPI_COMM_SIZE  (<comm>,  PETOT    ,  ierr)  call  MPI_COMM_RANK  (<comm>,  my_rank,  ierr)    if  (my_rank.eq.0)  then  open  (21,  file=  'a1x.all',  status=  'unknown')  do  i=  1,  NG  read  (21,*)  VECg(i)  enddo  close  (21)  endif  

#include  <mpi.h>  #include  <stdio.h>  #include  <math.h>  #include  <assert.h>    int  main(int  argc,  char  **argv){      int  i,  NG=32;      int  PeTot,  MyRank,  MPI_Comm;      double  VECg[32];      char  filename[80];      FILE  *fp;          MPI_Init(&argc,  &argv);      MPI_Comm_size(<comm>,  &PeTot);      MPI_Comm_rank(<comm>,  &MyRank);                      fp  =  fopen("a1x.all",  "r");                  if(!MyRank)  for(i=0;i<NG;i++){                                  fscanf(fp,  "%lf",  &VECg[i]);                  }  

82

Fundamental MPI

Scatter/Gatherの計算 （2/8）
4つのプロセスへ均等に（長さ8ずつ）割り振る。	
• MPI_Scatter の利用	

83

Fundamental MPIMPI_Scatter

P#0 A0 B0 C0 D0
P#1
P#2
P#3

•

P#0 A0
Scatter
GatherP#1 B0P#2 C0P#3 D0

コミュニケーター 「comm」内の一つの送信元プロセス「root」の送信バッファ「sendbuf」から各プロセスに先頭から「scount」ずつのサイズのメッセージを送信
し，その他全てのプロセスの受信バッファ「recvbuf」に，サイズ「ｒcount」のメッセージを格納。

• call MPI_SCATTER (sendbuf, scount, sendtype, recvbuf,
rcount, recvtype, root, comm, ierr)
–
–
–
–
–
–
–
–
–

sendbuf 任意
scount整数
sendtype 整数
recvbuf 任意
rcount整数
recvtype 整数
root
整数
comm
整数
ierr
整数

I
I
I
O
I
I
I
I
O

送信バッファの先頭アドレス，
送信メッセージのサイズ
送信メッセージのデータタイプ
受信バッファの先頭アドレス，
受信メッセージのサイズ
受信メッセージのデータタイプ
送信プロセスのID（ランク）
コミュニケータを指定する
完了コード	

84

Fundamental MPIMPI_Scatter（続き）	

P#0 A0 B0 C0 D0
P#1
P#2
P#3

P#0 A0
Scatter
GatherP#1 B0P#2 C0P#3 D0• call MPI_SCATTER (sendbuf, scount, sendtype, recvbuf,
rcount, recvtype, root, comm, ierr)
–
–
–
–
–
–
–
–
–

•

sendbuf 任意
scount整数
sendtype 整数
recvbuf 任意
rcount整数
recvtype 整数
root
整数
comm
整数
ierr
整数

I
I
I
O
I
I
I
I
O

送信バッファの先頭アドレス，
送信メッセージのサイズ
送信メッセージのデータタイプ
受信バッファの先頭アドレス，
受信メッセージのサイズ
受信メッセージのデータタイプ
送信プロセスのID（ランク）
コミュニケータを指定する
完了コード

通常は
– scount = rcount– sendtype= recvtype•

この関数によって，プロセスroot番のsendbuf（送信バッファ）の先頭アドレスから
scount個ずつの成分が，commで表されるコミュニケータを持つ各プロセスに送信され，recvbuf（受信バッファ）のrcount個の成分として受信される。

85

Fundamental MPI

Scatter/Gatherの計算 （3/8）
4つのプロセスへ均等に（長さ8ずつ）割り振る。	
• 各プロセスにおいて長さ8の受信バッファ「VEC」（＝局所データ）を定義しておく。
• プロセス0番から送信される送信バッファ「VECg」の8個ずつの成分が，4つの各プロセスにおいて受信バッファ「VEC」の1番目から8番目の成分として受信される• N=8 として引数は下記のようになる：	
integer, parameter :: N=8
real(kind=8), dimension(N)::VEC...
call MPI_SCATTER&(VECg, N, MPI_DOUBLE_PRECISION, &VEC , N, MPI_DOUBLE_PRECISION, &0, <comm>, ierr)	

int N=8;
double VEC[8];...
MPI_Scatter(&VECg, N, MPI_DOUBLE,&VEC , N, MPI_DOUBLE, 0, <comm>);

MPI_SCATTER(sendbuf, scount, sendtype, recvbuf, rcount,recvtype, root, comm, ierr)86

Fundamental MPI

Scatter/Gatherの計算 （4/8）
4つのプロセスへ均等に（長さ8ずつ）割り振る。	
• rootプロセス（0番）から各プロセスへ8個ずつの成分がscatterされる。
• VECgの1番目から8番目の成分が0番プロセスにおけるVECの1番目から8番目，9番目から16番目の成分が1番プロセスにおけるVECの1番目
から8番目という具合に格納される。
– VECg：全体データ，VEC：局所データ
VEC
recvbuf

8

8

8

8

PE#0

PE#1

PE#2

PE#3

8

8

8
VECg
sendbuf8

局所データ
local data

全体データ
root global data

87

Fundamental MPI

Scatter/Gatherの計算 （5/8）
4つのプロセスへ均等に（長さ8ずつ）割り振る。	
• 全体データ（global data）としてはVECgの1番から32番までの要素番号を持っていた各成分が，それぞれのプロセスにおける局所データ（local
data）としては，VECの1番から8番までの局所番号を持った成分として格納される。VECの成分を各プロセスごとに書き出してみると：
do  i=  1,  N      write  (*,'(a,  2i8,f10.0)')  'before',    my_rank,  i,  VEC(i)  enddo  

for(i=0;i<N;i++){  printf("before  %5d  %5d  %10.0F\n",  MyRank,  i+1,  VEC[i]);}  88

Fundamental MPI

Scatter/Gatherの計算 （5/8）
4つのプロセスへ均等に（長さ8ずつ）割り振る。	
• 全体データ（global data）としてはVECgの1番から32番までの要素番号を持っていた各成分が，それぞれのプロセスにおける局所データ（local
data）としては，VECの1番から8番までの局所番号を持った成分として格納される。VECの成分を各プロセスごとに書き出してみると：

PE#0  
before  0  1    101.  
before  0  2    103.  
before  0  3    105.  
before  0  4    106.  
before  0  5    109.  
before  0  6    111.  
before  0  7    121.  
before  0  8    151.  

PE#1  
before  1  1    201.  
before  1  2    203.  
before  1  3    205.  
before  1  4    206.  
before  1  5    209.  
before  1  6    211.  
before  1  7    221.  
before  1  8    251.  

PE#2  
before  2  1    301.  
before  2  2    303.  
before  2  3    305.  
before  2  4    306.  
before  2  5    309.  
before  2  6    311.  
before  2  7    321.  
before  2  8    351.  

PE#3  
before  3  1    401.  
before  3  2    403.  
before  3  3    405.  
before  3  4    406.  
before  3  5    409.  
before  3  6    411.  
before  3  7    421.  
before  3  8    451.  

89Fundamental MPI

Scatter/Gatherの計算 （6/8）
各プロセスでベクトル（長さ8）の各成分にALPHAを加える	
• 各プロセスでの計算は，以下のようになる：	
real(kind=8),  parameter  ::  ALPHA=  1000.  
do  i=  1,  N      VEC(i)=  VEC(i)  +  ALPHA  enddo  •

double  ALPHA=1000.;  ...  
for(i=0;i<N;i++){      VEC[i]=  VEC[i]  +  ALPHA;}  計算結果は以下のようになる：	
PE#0  
after  0  1    1101.  
after  0  2    1103.  
after  0  3    1105.  
after  0  4    1106.  
after  0  5    1109.  
after  0  6    1111.  
after  0  7    1121.  
after  0  8    1151.  

PE#1  
after  1  1    1201.  
after  1  2    1203.  
after  1  3    1205.  
after  1  4    1206.  
after  1  5    1209.  
after  1  6    1211.  
after  1  7    1221.  
after  1  8    1251.  

PE#2  
after  2  1    1301.  
after  2  2    1303.  
after  2  3    1305.  
after  2  4    1306.  
after  2  5    1309.  
after  2  6    1311.  
after  2  7    1321.  
after  2  8    1351.  

PE#3  
after  3  1    1401.  
after  3  2    1403.  
after  3  3    1405.  
after  3  4    1406.  
after  3  5    1409.  
after  3  6    1411.  
after  3  7    1421.  
after  3  8    1451.  

90Fundamental MPI

Scatter/Gatherの計算 （7/8）
各プロセスの結果を再び長さ32のベクトルにまとめる	
• これには，MPI_Scatter と丁度逆の MPI_Gather という関数が用意されている。	

91

Fundamental MPI

MPI_Gather

P#0 A0 B0 C0 D0
P#1
P#2
P#3

•

P#0 A0
Scatter
GatherP#1 B0P#2 C0P#3 D0MPI_Scatterの逆• call MPI_GATHER (sendbuf, scount, sendtype, recvbuf,
rcount, recvtype, root, comm, ierr)
–
–
–
–
–
–
–
–
–

•

sendbuf 任意
scount整数
sendtype 整数
recvbuf 任意
rcount整数
recvtype 整数
root
整数
comm
整数
ierr
整数

I
I
I
O
I
I
I
I
O

送信バッファの先頭アドレス，
送信メッセージのサイズ
送信メッセージのデータタイプ
受信バッファの先頭アドレス，
受信メッセージのサイズ
受信メッセージのデータタイプ
受信プロセスのID（ランク）
コミュニケータを指定する
完了コード

ここで，受信バッファ recvbuf の値はroot番のプロセスに集められる。	

92

Fundamental MPI

Scatter/Gatherの計算 （8/8）
各プロセスの結果を再び長さ32のベクトルにまとめる	
• 本例題の場合，root=0として，各プロセスから送信されるVECの成分を0番プロセスにおいてVECgとして受信するものとすると以下のようになる：
call MPI_GATHER&(VEC , N, MPI_DOUBLE_PRECISION, &VECg, N, MPI_DOUBLE_PRECISION, &0, <comm>, ierr)	

•MPI_Gather(&VEC , N, MPI_DOUBLE,&VECg, N, MPI_DOUBLE, 0, <comm>);

各プロセスから8個ずつの成分がrootプロセスへgatherされる	
VEC
sendbuf

8

8

8

8

PE#0

PE#1

PE#2

PE#3

8

8

8
VECg
recvbuf8

局所データ
local data

全体データ
root global data

Fundamental MPI<$FVM>/S1/scatter-gather.f
実行例	
$> mpicc –Os -noparallel scatter-gather.c$> mpif90 –Oss -noparallel scatter-gather.f$> 実行（4プロセス） go4.sh	
PE#0  
before  0  1    101.  
before  0  2    103.  
before  0  3    105.  
before  0  4    106.  
before  0  5    109.  
before  0  6    111.  
before  0  7    121.  
before  0  8    151.  

PE#1  
before  1  1    201.  
before  1  2    203.  
before  1  3    205.  
before  1  4    206.  
before  1  5    209.  
before  1  6    211.  
before  1  7    221.  
before  1  8    251.  

PE#2  
before  2  1    301.  
before  2  2    303.  
before  2  3    305.  
before  2  4    306.  
before  2  5    309.  
before  2  6    311.  
before  2  7    321.  
before  2  8    351.  

PE#3  
before  3  1    401.  
before  3  2    403.  
before  3  3    405.  
before  3  4    406.  
before  3  5    409.  
before  3  6    411.  
before  3  7    421.  
before  3  8    451.  

PE#0  
after  0  1    1101.  
after  0  2    1103.  
after  0  3    1105.  
after  0  4    1106.  
after  0  5    1109.  
after  0  6    1111.  
after  0  7    1121.  
after  0  8    1151.  

PE#1  
after  1  1    1201.  
after  1  2    1203.  
after  1  3    1205.  
after  1  4    1206.  
after  1  5    1209.  
after  1  6    1211.  
after  1  7    1221.  
after  1  8    1251.  

PE#2  
after  2  1    1301.  
after  2  2    1303.  
after  2  3    1305.  
after  2  4    1306.  
after  2  5    1309.  
after  2  6    1311.  
after  2  7    1321.  
after  2  8    1351.  

PE#3  
after  3  1    1401.  
after  3  2    1403.  
after  3  3    1405.  
after  3  4    1406.  
after  3  5    1409.  
after  3  6    1411.  
after  3  7    1421.  
after  3  8    1451.  

93

94

Fundamental MPI

MPI_Reduce_scatter
P#0 A0 B0 C0 D0P#1 A1 B1 C1 D1•P#0 op.A0-A3
Reduce scatterP#1 op.B0-B3

P#2 A2 B2 C2 D2P#2 op.C0-C3

P#3 A3 B3 C3 D3P#3 op.D0-D3

MPI_Reduce + MPI_Scatter• call MPI_REDUCE_SCATTER (sendbuf, recvbuf, rcount,datatype, op, comm, ierr)
–
–
–
–
–
–
–

sendbuf 任意
recvbuf 任意
rcount整数
datatype 整数
op
整数
comm
整数
ierr
整数

I
O
I
I
I
I
O

送信バッファの先頭アドレス，
受信バッファの先頭アドレス，
受信メッセージのサイズ（配列：サイズ＝プロセス数）
メッセージのデータタイプ
計算の種類
コミュニケータを指定する
完了コード

95

Fundamental MPIMPI_Allgather•

P#0 A0P#1 B0P#0 A0 B0 C0 D0
All gather

P#1 A0 B0 C0 D0P#2 C0P#2 A0 B0 C0 D0P#3 D0P#3 A0 B0 C0 D0

MPI_Gather＋MPI_Bcast– Gatherしたものを，全てのPEにBcastする（各プロセスで同じデータを持つ）• call MPI_ALLGATHER (sendbuf, scount, sendtype, recvbuf,
rcount, recvtype, comm, ierr)
–
–
–
–
–
–
–
–

sendbuf 任意
scount整数
sendtype 整数
recvbuf 任意
rcount整数
recvtype 整数
comm
整数
ierr
整数

I
I
I
O
I
I
I
O

送信バッファの先頭アドレス，
送信メッセージのサイズ
送信メッセージのデータタイプ
受信バッファの先頭アドレス，
受信メッセージのサイズ
受信メッセージのデータタイプ
コミュニケータを指定する
完了コード

96

Fundamental MPIMPI_Alltoall•

P#0 A0 B0 C0 D0

P#0 A0 A1 A2 A3
P#1 B0 B1 B2 B3All-to-All

P#1 A1 B1 C1 D1P#2 C0 C1 C2 C3P#2 A2 B2 C2 D2P#3 D0 D1 D2 D3P#3 A3 B3 C3 D3MPI_Allgatherの更なる拡張：転置• call MPI_ALLTOALL (sendbuf, scount, sendtype, recvbuf,
rcount, recvrype, comm, ierr)
–
–
–
–
–
–
–
–

sendbuf 任意
scount整数
sendtype 整数
recvbuf 任意
rcount整数
recvtype 整数
comm
整数
ierr
整数

I
I
I
O
I
I
I
O

送信バッファの先頭アドレス，
送信メッセージのサイズ
送信メッセージのデータタイプ
受信バッファの先頭アドレス，
受信メッセージのサイズ
受信メッセージのデータタイプ
コミュニケータを指定する
完了コード

97

Fundamental MPI

グループ通信による計算例	
• ベクトルの内積• Scatter/Gather• 分散ファイルの読み込み

Fundamental MPI

98

分散ファイルを使用したオペレーション	
• Scatter/Gatherの例では，PE#0から全体データを読み込み，それを全体にScatterして並列計算を実施した。
• 問題規模が非常に大きい場合，1つのプロセッサで全てのデータを読み込むことは不可能な場合がある。
– 最初から分割しておいて，「局所データ」を各プロセッサで独立に読み込む– あるベクトルに対して，全体操作が必要になった場合は，状況に応じてMPI_Gatherなどを使用する

99

Fundamental MPI

分散ファイル読み込み：等データ長（1/2）	
>$ cd <$FVM>/S1>$ ls a1.*
a1.0 a1.1 a1.2 a1.3

「a1x.all」を4つに分割したもの>$ mpicc –Os -noparallel file.c>$ mpif90 –Oss -noparallel file.f>$ 実行：4プロセス go4.sh	100

Fundamental MPI

分散ファイルの操作	
• 「a1.0~a1.3」は全体ベクトル「a1x.all」を領域に分割したもの，と考えることができる。	
a1x.all

a1.0

a1.1

a1.2

a1.3

101

Fundamental MPI

分散ファイル読み込み：等データ長（2/2）	
<$FVM>/S1/file.f
implicit REAL*8 (A-H,O-Z)
include 'mpif.h'
integer :: PETOT, my_rank, ierrreal(kind=8), dimension(8) :: VEC
character(len=80):: filename

Hello とそんなに変わらない	

call MPI_INIT(ierr)
call MPI_COMM_SIZE (MPI_COMM_WORLD, PETOT, ierr )call MPI_COMM_RANK (MPI_COMM_WORLD, my_rank, ierr )if (my_rank.eq.0) filename= 'a1.0'
if (my_rank.eq.1) filename= 'a1.1'
if (my_rank.eq.2) filename= 'a1.2'
if (my_rank.eq.3) filename= 'a1.3'
open (21, file= filename, status= 'unknown')
do i= 1, 8
read (21,*) VEC(i)enddoclose (21)call MPI_FINALIZE (ierr)
stop
end

「局所番号（1~8）」で
読み込む	

102

Fundamental MPISPMDの典型例	

PE #0

PE #1

PE #2

PE #3

“a.out”“a.out”“a.out”“a.out”“a1.0”“a1.1”“a1.2”“a1.3”

mpirun -np 4 a.out

Fundamental MPI

分散ファイル読み込み：可変長（1/2）	
>$ cd <$FVM>/S1>$ ls a2.*
a2.0 a2.1 a2.2 a2.3>$ cat a2.0
5
各PEにおける成分数
201.0
成分の並び
203.0
205.0
206.0
209.0
>$ mpicc –Os -noparallel file2.c>$ mpif90 –Oss -noparallel file2.f>$ 実行：4プロセス go4.sh	103

Fundamental MPI

分散ファイルの読み込み：可変長（2/2）	
<$FVM>/S1/file2.f
implicit REAL*8 (A-H,O-Z)
include 'mpif.h'
integer :: PETOT, my_rank, ierrreal(kind=8), dimension(:), allocatable :: VEC
character(len=80):: filename
call MPI_INIT(ierr)
call MPI_COMM_SIZE (MPI_COMM_WORLD, PETOT, ierr )call MPI_COMM_RANK (MPI_COMM_WORLD, my_rank, ierr )if (my_rank.eq.0) filename= 'a2.0'
if (my_rank.eq.1) filename= 'a2.1'
if (my_rank.eq.2) filename= 'a2.2'
if (my_rank.eq.3) filename= 'a2.3'
open (21, file= filename, status= 'unknown')
read (21,*) N
allocate (VEC(N))
do i= 1, N
Nが各データ（プロセッサ）で異なる	
read (21,*) VEC(i)enddoclose(21)call MPI_FINALIZE (ierr)
stop
end

104

Fundamental MPI

局所データの作成法	
• 全体データ（N=NG）を入力	
– Scatterして各プロセスに分割	
– 各プロセスで演算	– 必要に応じて局所データをGather（またはAllgather）して全体デー
タを生成	• 局所データ（N=NL）を生成，あるいは（あらかじめ分割生成
して）入力	
– 各プロセスで局所データを生成，あるいは入力	
– 各プロセスで演算	– 必要に応じて局所データをGather（またはAllgather）して全体デー
タを生成	• 将来的には後者が中心となるが，全体的なデータの動きを理解するために，しばらくは前者についても併用	
105

Fundamental MPI• MPIとは• MPIの基礎：Hello World• 全体データと局所データ• グループ通信（Collective Communication）	
• 1対1通信（Point-to-Point Communication）	

106

Fundamental MPI

107

有限体積法：全体マトリクスの生成
要素iに関する釣り合い	
⎡
⎤
⎡
⎤
⎢
⎢
⎥
S ik
Sie ⎥
SikSie
⎢⎥Ti − ⎢+
Tk ⎥ =
Sid qid + Vi Qi +TiBe
d ie ⎥d ie⎢ k d ik + d ki
⎢ k d ik + d ki ⎥ d
e
e
⎢⎣
⎢⎣
λi λk
λi ⎥⎦λi λk ⎥⎦
λi

∑

∑

∑

D（対角成分）	

∑AMAT（非対角成分）	

隣接要素の情報必要	a

BFORCE（右辺）	

自分自身（要素i）の情報のみ必要	

b
dai

dbi

Sib
Sia

dia

die

TiBe

∑

Sie
Sid

dib

i

dic
dci

Sic
qid

c

2008-08-29-CS10

Fundamental MPI

前処理付き共役勾配法
Preconditioned Conjugate Gradient Method（CG）	
Compute r(0)= b-[A]x(0)for i= 1, 2, …
solve [M]z(i-1)= r(i-1)ρi-1= r(i-1) z(i-1)
if i=1p(1)= z(0)else
βi-1= ρi-1/ρi-2p(i)= z(i-1) + βi-1 p(i-1)
endifq(i)= [A]p(i)αi = ρi-1/p(i)q(i)x(i)= x(i-1) +αip(i)r(i)= r(i-1) - αiq(i)check convergence |r|end	

前処理：対角スケーリング		
行列ベクトル積：	
領域外の値が必要	

108

Fundamental MPI

1対1通信とは ?
• グループ通信：Collective Communication– MPI_Reduce, MPI_Scatter/Gather など	– 同じコミュニケータ内の全プロセスと通信する	
– 適用分野	• 境界要素法，スペクトル法，分子動力学等グローバルな相互作用のある手法	
• 内積，最大値などのオペレーション	
 #PE2• 1対1通信：Point-to-Point– MPI_Send, MPI_Receive– 特定のプロセスとのみ通信がある	

21

22

23

24

16

17

18

19

11

12

13

14

6

7

8

#PE1

• 隣接領域	

– 適用分野	• 差分法，有限要素法などローカルな情報を使う手法	

11

12

13

6

7

8

9

10

1

2

3

4

5

23

24

25

18

19

20

13

14

15

8

9

10

4

5

#PE0

109

Fundamental MPI

1対1通信の方法	
• MPI_Send， MPI_Recvというサブルーチンがある。	
• しかし，これらは「ブロッキング（blocking）」通信サブルーチン
で，デッドロック（dead lock）を起こしやすい。	
– 受信（Recv）の完了が確認されないと，送信（Send）が終了しない	

• もともと非常に「secureな」通信を保障するために，MPI仕様の中に入れられたものであるが，実用上は不便この上ない。	
– したがって実際にアプリケーションレベルで使用されることはほとん
ど無い（と思う）。	
– 将来にわたってこの部分が改正される予定はないらしい。	

• 「そういう機能がある」ということを心の片隅においておいてく
ださい。	
110

Fundamental MPIMPI_Send/MPI_Recv
PE#0
1

2

3

if (my_rank.eq.0) NEIB_ID=1
if (my_rank.eq.1) NEIB_ID=0

4

5…
call MPI_SEND (NEIB_ID, arg’s)
call MPI_RECV (NEIB_ID, arg’s)
…

PE#1
4

1

2

3

4

• 例えば先ほどの例で言えば，このようにしたいところであるが，このようなプログラムを作るとMPI_Send/
MPI_Recvのところで止まってしまう。	
– 動く場合もある111

Fundamental MPIMPI_Send/MPI_Recv （続き）	
if (my_rank.eq.0) NEIB_ID=1
if (my_rank.eq.1) NEIB_ID=0

PE#0
1

2

3

4

…
if (my_rank.eq.0) thencall MPI_SEND (NEIB_ID, arg’s)
call MPI_RECV (NEIB_ID, arg’s)
endif

5

PE#1
4

1

2

3

4

if (my_rank.eq.1) thencall MPI_RECV (NEIB_ID, arg’s)
call MPI_SEND (NEIB_ID, arg’s)
endif…

• このようにすれば，動く。	

112

Fundamental MPI

1対1通信の方法（実際どうするか）	
• MPI_Isend， MPI_Irecv，という「ブロッキングしない（non-blocking）」サブルーチンがある。これと，同期のための「MPI_Waitall」を組み合わせる。	
• MPI_Sendrecv というサブルーチンもある（後述）。	
if (my_rank.eq.0) NEIB_ID=1
if (my_rank.eq.1) NEIB_ID=0

PE#0
1

2

3

4

…
call MPI_ISEND (NEIB_ID, arg’s)
call MPI_IRECV (NEIB_ID, arg’s)…
call MPI_WAITALL (for IRECV)…
call MPI_WAITALL (for ISEND)

5

PE#1
4

1

2

3

4

IsendとIrecvで同じ通信識別子を使って，	
更に整合性が取れるのであればWaitallは	
一箇所でもOKです（後述）	
113

Fundamental MPI

MPI_Isend	•

送信バッファ「sendbuf」内の，連続した「count」個の送信メッセージを，タグ「tag」
を付けて，コミュニケータ内の，「dest」に送信する。「MPI_Waitall」を呼ぶまで，送信バッファの内容を更新してはならない。	

• call MPI_ISEND(sendbuf,count,datatype,dest,tag,comm,request, ierr)sendbuf 任意
count
整数
datatype 整数
dest
整数
tag
整数
	
 	
	
– comm整数– request 整数
	
 	
	
	
 	
	
– ierr
整数
–
–
–
–
–

	
I
	
I
	
I
	
I
	
I
	
	
I
	
O
	
	
	
O

送信バッファの先頭アドレス，	メッセージのサイズ	
メッセージのデータタイプ	
宛先プロセスのアドレス（ランク）	
メッセージタグ，送信メッセージの種類を区別するときに使用。	
	
通常は「0」でよい。同じメッセージタグ番号同士で通信。	
コミュニケータを指定する	
通信識別子。MPI_WAITALLで使用。	
	（配列：サイズは同期する必要のある「MPI_ISEND」呼び出し		
 数（通常は隣接プロセス数など））:C言語については後述	
完了コード	
114

Fundamental MPI

通信識別子（request handle）： request• call MPI_ISEND(sendbuf,count,datatype,dest,tag,comm,request, ierr)sendbuf 任意
count
整数
datatype 整数
dest
整数
tag
整数
	
 	
	
– comm整数– request 整数
	
 	
	
	
 	
	
– ierr
整数
–
–
–
–
–

•

	
I
	
I
	
I
	
I
	
I
	
	
I
	
O
	
	
	
O

送信バッファの先頭アドレス，	メッセージのサイズ	
メッセージのデータタイプ	
宛先プロセスのアドレス（ランク）	
メッセージタグ，送信メッセージの種類を区別するときに使用。	
	
通常は「0」でよい。同じメッセージタグ番号同士で通信。	
コミュニケータを指定する	
通信識別子。MPI_WAITALLで使用。	
	（配列：サイズは同期する必要のある「MPI_ISEND」呼び出し		
 数（通常は隣接プロセス数など））	
完了コード	

以下のような形で宣言しておく（記憶領域を確保するだけで良い）	
allocate (request(NEIBPETOT))	

115

Fundamental MPI

MPI_Irecv•

受信バッファ「recvbuf」内の，連続した「count」個の送信メッセージを，タグ「tag」
を付けて，コミュニケータ内の，「dest」から受信する。「MPI_Waitall」を呼ぶまで，受信バッファの内容を利用した処理を実施してはならない。	

• call MPI_IRECV(recvbuf,count,datatype,dest,tag,comm,request, ierr)recvbuf 任意
count
整数
datatype 整数
dest
整数
tag
整数
	
 	
	
– comm整数– request 整数
	
 	
	
	
 	
	
– ierr
整数
–
–
–
–
–

	
I
	
I
	
I
	
I
	
I
	
	
I
	
O
	
	
	
O

受信バッファの先頭アドレス，	メッセージのサイズ	
メッセージのデータタイプ	
宛先プロセスのアドレス（ランク）	
メッセージタグ，受信メッセージの種類を区別するときに使用。	
	
通常は「0」でよい。同じメッセージタグ番号同士で通信。	
コミュニケータを指定する	
通信識別子。MPI_WAITALLで使用。	
	（配列：サイズは同期する必要のある「MPI_IRECV」呼び出し		
 数（通常は隣接プロセス数など））：C言語については後述	
完了コード	
116

Fundamental MPI

MPI_Waitall•••

1対1非ブロッキング通信サブルーチンである「MPI_ISEND」と「MPI_IRECV」を使用
した場合，プロセスの同期を取るのに使用する。	
送信時はこの「MPI_WAITALL」を呼ぶ前に送信バッファの内容を変更してはならない。
受信時は「MPI_WAITALL」を呼ぶ前に受信バッファの内容を利用してはならない。	
整合性が取れていれば， 「MPI_ISEND」と「MPI_IRECV」を同時に同期してもよい。	
– 「MPI_ISEND/IRECV」で同じ通信識別子を使用すること	

•「MPI_BARRIER」と同じような機能であるが，代用はできない。	
– 実装にもよるが，「request」，「status」の内容が正しく更新されず，何度も「MPI_ISEND/IRECV」を呼び出すと処理が遅くなる，というような経験もある。	

• call MPI_WAITALL– count
整数– request 整数		– status整数– ierr(count,request,status,ierr)

	
I
	
I/O
	
	
O

	

	

	
整数

	
	
O

同期する必要のある「MPI_ISEND」 ，「MPI_RECV」呼び出し数。	
通信識別子。「MPI_ISEND」，「MPI_IRECV」で利用した識別		
子名に対応。（配列サイズ：（count））	
状況オブジェクト配列（配列サイズ：（MPI_STATUS_SIZE,count））	
	
MPI_STATUS_SIZE： “mpif.h”,”mpi.h”で定められる		
パラメータ：C言語については後述	
完了コード	
117

Fundamental MPI

状況オブジェクト配列（status object）：
status• call MPI_WAITALL– count
整数– request 整数		– status整数– ierr•(count,request,status,ierr)

	
I
	
I/O
	
	
O

	

	

	
整数

	
	
O

同期する必要のある「MPI_ISEND」 ，「MPI_RECV」呼び出し数。	
通信識別子。「MPI_ISEND」，「MPI_IRECV」で利用した識別		
子名に対応。（配列サイズ：（count））	
状況オブジェクト配列（配列サイズ：（MPI_STATUS_SIZE,count））	
	
MPI_STATUS_SIZE： “mpif.h”,”mpi.h”で定められる		
パラメータ	
完了コード	

以下のように予め記憶領域を確保しておくだけでよい：	
allocate (stat(MPI_STATUS_SIZE,NEIBPETOT))	

118

Fundamental MPIMPI_Sendrecv•

MPI_Send+MPI_Recv：制約多い• call MPI_SENDRECV(sendbuf,sendcount,sendtype,dest,sendtag,recvbuf,
recvcount,recvtype,source,recvtag,comm,status,ierr)sendbuf任意
sendcount 整数
sendtype 整数
dest
整数
sendtag整数	 		– recvbuf
任意– recvcount 整数– recvtype 整数– source整数– sendtag
整数	 		– comm整数– status整数	– ierr
整数
–
–
–
–
–

	
I
	
I
	
I
	
I
	
I
	
	
I
	
I
	
I
	
I
	
I
	
	
I
	
O
	
	
O

送信バッファの先頭アドレス，	
送信メッセージのサイズ	
送信メッセージのデータタイプ	
宛先プロセスのアドレス（ランク）	
送信用メッセージタグ，送信メッセージの種類を区別するときに使用。	
	
通常は「0」でよい。	
受信バッファの先頭アドレス，	
受信メッセージのサイズ	
受信メッセージのデータタイプ	
送信元プロセスのアドレス（ランク）	
受信用メッセージタグ，送信メッセージの種類を区別するときに使用。	
	
通常は「0」でよい。同じメッセージタグ番号同士で通信。	
コミュニケータを指定する	
状況オブジェクト配列（配列サイズ：（MPI_STATUS_SIZE））	
	
MPI_STATUS_SIZE： “mpif.h”で定められるパラメータ	
完了コード	
119

Fundamental MPIRECV（受信）：外点への受信
受信バッファに隣接プロセスから連続したデータを受け取る	
• MPI_Irecv(recvbuf,count,datatype,dest,tag,comm,request)––––

recvbuf 任意
count
整数
datatype 整数
dest
整数

	
I
	
I
	
I
	
I

受信バッファの先頭アドレス，	メッセージのサイズ	
メッセージのデータタイプ	
宛先プロセスのアドレス（ランク）	
#PE2
21

22

23

24

16

17

18

19

11

12

13

14

6

7

8

#PE1

11

12

13

6

7

8

9

10

1

2

3

4

5

23

24

25

18

19

20

13

14

15

8

9

10

4

5

#PE0

120

Fundamental MPI

SEND（送信）：境界点の送信
送信バッファの連続したデータを隣接プロセスに送る	
• MPI_Isend(sendbuf,count,datatype,dest,tag,comm,request)––––

sendbuf 任意
count
整数
datatype 整数
dest
整数

	
I
	
I
	
I
	
I

送信バッファの先頭アドレス，	メッセージのサイズ	
メッセージのデータタイプ	
宛先プロセスのアドレス（ランク）	
#PE2
21

22

23

24

16

17

18

19

11

12

13

14

6

7

8

#PE1

11

12

13

6

7

8

9

10

1

2

3

4

5

23

24

25

18

19

20

13

14

15

8

9

10

4

5

#PE0

121

Fundamental MPI

通信識別子，状況オブジェクト配列の定義の仕方（FORTRAN）	
• MPI_Isend: request• MPI_Irecv: request• MPI_Waitall: request, status
integer request(NEIBPETOT)
integer status (MPI_STAUTS_SIZE,NEIBPETOT)

122

Fundamental MPI

通信識別子，状況オブジェクト配列の定義の仕方（C）：特殊な変数の型がある	
• MPI_Isend: request• MPI_Irecv: request• MPI_Waitall: request, status
MPI_Status *StatSend, *StatRecv;
MPI_Request *RequestSend, *RequestRecv;
・・・	
StatSend = malloc(sizeof(MPI_Status) * NEIBpetot);
StatRecv = malloc(sizeof(MPI_Status) * NEIBpetot);
RequestSend = malloc(sizeof(MPI_Request) * NEIBpetot);
RequestRecv = malloc(sizeof(MPI_Request) * NEIBpetot);

123

Fundamental MPI

利用例（1）：スカラー送受信	• PE#0，PE#1間 で8バイト実数VALの値を交換する。	
if (my_rank.eq.0) NEIB= 1
if (my_rank.eq.1) NEIB= 0
call MPI_ISEND (VAL
,1,MPI_DOUBLE_PRECISION,NEIB,…,req_send,…)call MPI_IRECV (VALtemp,1,MPI_DOUBLE_PRECISION,NEIB,…,req_recv,…)
call MPI_WAITALL (…,req_recv,stat_recv,…):受信バッファ VALtemp を利用可能	
call MPI_WAITALL (…,req_send,stat_send,…)：送信バッファ VAL を変更可能	
VAL= VALtemp

受信バッファ名を「VAL」にしても動く場合はあるが，お勧めはしない。	

124

利用例（1）：スカラー送受信 FORTRAN

Fundamental MPIIsend/Irecv/Waitall$> cd <$FVM>/S2$> mpicc –Os -noparallel ex1-1.c$> mpif90 –Oss -noparallel ex1-1.f$> 実行（2プロセス） go2.sh	
implicit REAL*8 (A-H,O-Z)	
include 'mpif.h'	
integer(kind=4) :: my_rank, PETOT, NEIB	
real (kind=8) :: VAL, VALtemp	
integer(kind=4), dimension(MPI_STATUS_SIZE,1) :: stat_send, stat_recv	
integer(kind=4), dimension(1):: request_send, request_recv	
	
call MPI_INIT(ierr)	
call MPI_COMM_SIZE (MPI_COMM_WORLD, PETOT, ierr )	
call MPI_COMM_RANK (MPI_COMM_WORLD, my_rank, ierr )	
	
if (my_rank.eq.0) then	NEIB= 1
VAL = 10.d0else	
NEIB= 0
VAL = 11.d0	
endif	
	call MPI_ISEND (VAL, 1,MPI_DOUBLE_PRECISION,NEIB,0,MPI_COMM_WORLD,request_send(1),ierr)	
call MPI_IRECV (VALx,1,MPI_DOUBLE_PRECISION,NEIB,0,MPI_COMM_WORLD,request_recv(1),ierr)	
call MPI_WAITALL (1, request_recv, stat_recv, ierr)	
call MPI_WAITALL (1, request_send, stat_send, ierr)	
VAL= VALx
	
call MPI_FINALIZE (ierr)	
end

125

Fundamental MPI

利用例（2）：配列の送受信（1/4）	
• PE#0，PE#1間 で8バイト実数配列VECの値を交換する。	
• PE#0⇒PE#1– PE#0：VEC（1）～VEC（11）の値を送る（長さ：11）	
– PE#1：VEV（26）~VEC（36）の値として受け取る	

• PE#1⇒PE#0– PE#1：VEC（1）～VEC（25）の値を送る（長さ：25）	
– PE#0：VEV（12）~VEC（36）の値として受け取る	

• 演習：プログラムを作成して見よう！	
PE#0

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

PE#1

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

126

Fundamental MPI

演 習	• VEC（:）の初期状態を以下のようにする：	
– PE#0 VEC（1-36）= 101,102,103,~,135,136– PE#1 VEC（1-36）= 201,202,203,~,235,236• 次ページのような結果になることを確認せよ	• 以下のそれぞれを使用したプログラムを作成せよ	
– MPI_Isend/Irecv/Waitall– MPI_Sendrecv

127予測される結果	0	
 #BEFORE#	
 	
 1	
 	
 	
 	
 	
 	
 101.	 0	
 #BEFORE#	
 	
 2	
 	
 	
 	
 	
 	
 102.	 0	
 #BEFORE#	
 	
 3	
 	
 	
 	
 	
 	
 103.	 0	
 #BEFORE#	
 	
 4	
 	
 	
 	
 	
 	
 104.	 0	
 #BEFORE#	
 	
 5	
 	
 	
 	
 	
 	
 105.	 0	
 #BEFORE#	
 	
 6	
 	
 	
 	
 	
 	
 106.	 0	
 #BEFORE#	
 	
 7	
 	
 	
 	
 	
 	
 107.	 0	
 #BEFORE#	
 	
 8	
 	
 	
 	
 	
 	
 108.	 0	
 #BEFORE#	
 	
 9	
 	
 	
 	
 	
 	
 109.	 0	
 #BEFORE#	
 10	
 	
 	
 	
 	
 	
 110.	 0	
 #BEFORE#	
 11	
 	
 	
 	
 	
 	
 111.	 0	
 #BEFORE#	
 12	
 	
 	
 	
 	
 	
 112.	 0	
 #BEFORE#	
 13	
 	
 	
 	
 	
 	
 113.	 0	
 #BEFORE#	
 14	
 	
 	
 	
 	
 	
 114.	 0	
 #BEFORE#	
 15	
 	
 	
 	
 	
 	
 115.	 0	
 #BEFORE#	
 16	
 	
 	
 	
 	
 	
 116.	 0	
 #BEFORE#	
 17	
 	
 	
 	
 	
 	
 117.	 0	
 #BEFORE#	
 18	
 	
 	
 	
 	
 	
 118.	 0	
 #BEFORE#	
 19	
 	
 	
 	
 	
 	
 119.	 0	
 #BEFORE#	
 20	
 	
 	
 	
 	
 	
 120.	 0	
 #BEFORE#	
 21	
 	
 	
 	
 	
 	
 121.	 0	
 #BEFORE#	
 22	
 	
 	
 	
 	
 	
 122.	 0	
 #BEFORE#	
 23	
 	
 	
 	
 	
 	
 123.	 0	
 #BEFORE#	
 24	
 	
 	
 	
 	
 	
 124.	 0	
 #BEFORE#	
 25	
 	
 	
 	
 	
 	
 125.	 0	
 #BEFORE#	
 26	
 	
 	
 	
 	
 	
 126.	 0	
 #BEFORE#	
 27	
 	
 	
 	
 	
 	
 127.	 0	
 #BEFORE#	
 28	
 	
 	
 	
 	
 	
 128.	 0	
 #BEFORE#	
 29	
 	
 	
 	
 	
 	
 129.	 0	
 #BEFORE#	
 30	
 	
 	
 	
 	
 	
 130.	 0	
 #BEFORE#	
 31	
 	
 	
 	
 	
 	
 131.	 0	
 #BEFORE#	
 32	
 	
 	
 	
 	
 	
 132.	 0	
 #BEFORE#	
 33	
 	
 	
 	
 	
 	
 133.	 0	
 #BEFORE#	
 34	
 	
 	
 	
 	
 	
 134.	 0	
 #BEFORE#	
 35	
 	
 	
 	
 	
 	
 135.	 0	
 #BEFORE#	
 36	
 	
 	
 	
 	
 	
 136.	
 0	
 #AFTER	
 #	
 	
 1	
 	
 	
 	
 	
 	
 101.	
 0	
 #AFTER	
 #	
 	
 2	
 	
 	
 	
 	
 	
 102.	
 0	
 #AFTER	
 #	
 	
 3	
 	
 	
 	
 	
 	
 103.	
 0	
 #AFTER	
 #	
 	
 4	
 	
 	
 	
 	
 	
 104.	
 0	
 #AFTER	
 #	
 	
 5	
 	
 	
 	
 	
 	
 105.	
 0	
 #AFTER	
 #	
 	
 6	
 	
 	
 	
 	
 	
 106.	
 0	
 #AFTER	
 #	
 	
 7	
 	
 	
 	
 	
 	
 107.	
 0	
 #AFTER	
 #	
 	
 8	
 	
 	
 	
 	
 	
 108.	
 0	
 #AFTER	
 #	
 	
 9	
 	
 	
 	
 	
 	
 109.	
 0	
 #AFTER	
 #	
 10	
 	
 	
 	
 	
 	
 110.	
 0	
 #AFTER	
 #	
 11	
 	
 	
 	
 	
 	
 111.	
 0	
 #AFTER	
 #	
 12	
 	
 	
 	
 	
 	
 201.	
 0	
 #AFTER	
 #	
 13	
 	
 	
 	
 	
 	
 202.	
 0	
 #AFTER	
 #	
 14	
 	
 	
 	
 	
 	
 203.	
 0	
 #AFTER	
 #	
 15	
 	
 	
 	
 	
 	
 204.	
 0	
 #AFTER	
 #	
 16	
 	
 	
 	
 	
 	
 205.	
 0	
 #AFTER	
 #	
 17	
 	
 	
 	
 	
 	
 206.	
 0	
 #AFTER	
 #	
 18	
 	
 	
 	
 	
 	
 207.	
 0	
 #AFTER	
 #	
 19	
 	
 	
 	
 	
 	
 208.	
 0	
 #AFTER	
 #	
 20	
 	
 	
 	
 	
 	
 209.	
 0	
 #AFTER	
 #	
 21	
 	
 	
 	
 	
 	
 210.	
 0	
 #AFTER	
 #	
 22	
 	
 	
 	
 	
 	
 211.	
 0	
 #AFTER	
 #	
 23	
 	
 	
 	
 	
 	
 212.	
 0	
 #AFTER	
 #	
 24	
 	
 	
 	
 	
 	
 213.	
 0	
 #AFTER	
 #	
 25	
 	
 	
 	
 	
 	
 214.	
 0	
 #AFTER	
 #	
 26	
 	
 	
 	
 	
 	
 215.	
 0	
 #AFTER	
 #	
 27	
 	
 	
 	
 	
 	
 216.	
 0	
 #AFTER	
 #	
 28	
 	
 	
 	
 	
 	
 217.	
 0	
 #AFTER	
 #	
 29	
 	
 	
 	
 	
 	
 218.	
 0	
 #AFTER	
 #	
 30	
 	
 	
 	
 	
 	
 219.	
 0	
 #AFTER	
 #	
 31	
 	
 	
 	
 	
 	
 220.	
 0	
 #AFTER	
 #	
 32	
 	
 	
 	
 	
 	
 221.	
 0	
 #AFTER	
 #	
 33	
 	
 	
 	
 	
 	
 222.	
 0	
 #AFTER	
 #	
 34	
 	
 	
 	
 	
 	
 223.	
 0	
 #AFTER	
 #	
 35	
 	
 	
 	
 	
 	
 224.	
 0	
 #AFTER	
 #	
 36	
 	
 	
 	
 	
 	
 225.	 1	 #BEFORE#	
 	
 1	
 	
 	
 	
 	
 	
 201.	 1	 #BEFORE#	
 	
 2	
 	
 	
 	
 	
 	
 202.	 1	 #BEFORE#	
 	
 3	
 	
 	
 	
 	
 	
 203.	 1	 #BEFORE#	
 	
 4	
 	
 	
 	
 	
 	
 204.	 1	 #BEFORE#	
 	
 5	
 	
 	
 	
 	
 	
 205.	 1	 #BEFORE#	
 	
 6	
 	
 	
 	
 	
 	
 206.	 1	 #BEFORE#	
 	
 7	
 	
 	
 	
 	
 	
 207.	 1	 #BEFORE#	
 	
 8	
 	
 	
 	
 	
 	
 208.	 1	 #BEFORE#	
 	
 9	
 	
 	
 	
 	
 	
 209.	 1	 #BEFORE#	
 10	
 	
 	
 	
 	
 	
 210.	 1	 #BEFORE#	
 11	
 	
 	
 	
 	
 	
 211.	 1	 #BEFORE#	
 12	
 	
 	
 	
 	
 	
 212.	 1	 #BEFORE#	
 13	
 	
 	
 	
 	
 	
 213.	 1	 #BEFORE#	
 14	
 	
 	
 	
 	
 	
 214.	 1	 #BEFORE#	
 15	
 	
 	
 	
 	
 	
 215.	 1	 #BEFORE#	
 16	
 	
 	
 	
 	
 	
 216.	 1	 #BEFORE#	
 17	
 	
 	
 	
 	
 	
 217.	 1	 #BEFORE#	
 18	
 	
 	
 	
 	
 	
 218.	 1	 #BEFORE#	
 19	
 	
 	
 	
 	
 	
 219.	 1	 #BEFORE#	
 20	
 	
 	
 	
 	
 	
 220.	 1	 #BEFORE#	
 21	
 	
 	
 	
 	
 	
 221.	 1	 #BEFORE#	
 22	
 	
 	
 	
 	
 	
 222.	 1	 #BEFORE#	
 23	
 	
 	
 	
 	
 	
 223.	 1	 #BEFORE#	
 24	
 	
 	
 	
 	
 	
 224.	 1	 #BEFORE#	
 25	
 	
 	
 	
 	
 	
 225.	 1	 #BEFORE#	
 26	
 	
 	
 	
 	
 	
 226.	 1	 #BEFORE#	
 27	
 	
 	
 	
 	
 	
 227.	 1	 #BEFORE#	
 28	
 	
 	
 	
 	
 	
 228.	 1	 #BEFORE#	
 29	
 	
 	
 	
 	
 	
 229.	 1	 #BEFORE#	
 30	
 	
 	
 	
 	
 	
 230.	 1	 #BEFORE#	
 31	
 	
 	
 	
 	
 	
 231.	 1	 #BEFORE#	
 32	
 	
 	
 	
 	
 	
 232.	 1	 #BEFORE#	
 33	
 	
 	
 	
 	
 	
 233.	 1	 #BEFORE#	
 34	
 	
 	
 	
 	
 	
 234.	 1	 #BEFORE#	
 35	
 	
 	
 	
 	
 	
 235.	 1	 #BEFORE#	
 36	
 	
 	
 	
 	
 	
 236.	
 Fundamental MPI

1	
 #AFTER	
 #	
 	
 1	
 	
 	
 	
 	
 	
 201.	
 1	 #AFTER	
 #	
 	
 2	
 	
 	
 	
 	
 	
 202.	
 1	 #AFTER	
 #	
 	
 3	
 	
 	
 	
 	
 	
 203.	
 1	 #AFTER	
 #	
 	
 4	
 	
 	
 	
 	
 	
 204.	
 1	 #AFTER	
 #	
 	
 5	
 	
 	
 	
 	
 	
 205.	
 1	 #AFTER	
 #	
 	
 6	
 	
 	
 	
 	
 	
 206.	
 1	 #AFTER	
 #	
 	
 7	
 	
 	
 	
 	
 	
 207.	
 1	 #AFTER	
 #	
 	
 8	
 	
 	
 	
 	
 	
 208.	
 1	 #AFTER	
 #	
 	
 9	
 	
 	
 	
 	
 	
 209.	
 1	 #AFTER	
 #	
 10	
 	
 	
 	
 	
 	
 210.	
 1	 #AFTER	
 #	
 11	
 	
 	
 	
 	
 	
 211.	
 1	 #AFTER	
 #	
 12	
 	
 	
 	
 	
 	
 212.	
 1	 #AFTER	
 #	
 13	
 	
 	
 	
 	
 	
 213.	
 1	 #AFTER	
 #	
 14	
 	
 	
 	
 	
 	
 214.	
 1	 #AFTER	
 #	
 15	
 	
 	
 	
 	
 	
 215.	
 1	 #AFTER	
 #	
 16	
 	
 	
 	
 	
 	
 216.	
 1	 #AFTER	
 #	
 17	
 	
 	
 	
 	
 	
 217.	
 1	 #AFTER	
 #	
 18	
 	
 	
 	
 	
 	
 218.	
 1	 #AFTER	
 #	
 19	
 	
 	
 	
 	
 	
 219.	
 1	 #AFTER	
 #	
 20	
 	
 	
 	
 	
 	
 220.	
 1	 #AFTER	
 #	
 21	
 	
 	
 	
 	
 	
 221.	
 1	 #AFTER	
 #	
 22	
 	
 	
 	
 	
 	
 222.	
 1	 #AFTER	
 #	
 23	
 	
 	
 	
 	
 	
 223.	
 1	 #AFTER	
 #	
 24	
 	
 	
 	
 	
 	
 224.	
 1	 #AFTER	
 #	
 25	
 	
 	
 	
 	
 	
 225.	
 1	 #AFTER	
 #	
 26	
 	
 	
 	
 	
 	
 101.	
 1	 #AFTER	
 #	
 27	
 	
 	
 	
 	
 	
 102.	
 1	 #AFTER	
 #	
 28	
 	
 	
 	
 	
 	
 103.	
 1	 #AFTER	
 #	
 29	
 	
 	
 	
 	
 	
 104.	
 1	 #AFTER	
 #	
 30	
 	
 	
 	
 	
 	
 105.	
 1	 #AFTER	
 #	
 31	
 	
 	
 	
 	
 	
 106.	
 1	 #AFTER	
 #	
 32	
 	
 	
 	
 	
 	
 107.	
 1	 #AFTER	
 #	
 33	
 	
 	
 	
 	
 	
 108.	
 1	 #AFTER	
 #	
 34	
 	
 	
 	
 	
 	
 109.	
 1	 #AFTER	
 #	
 35	
 	
 	
 	
 	
 	
 110.	
 1	 #AFTER	
 #	
 36	
 	
 	
 	
 	
 	
 111.	
 128

Fundamental MPI

利用例（2）：配列の送受信（2/4）	
if (my_rank.eq.0) thencall MPI_ISEND (VEC( 1),11,MPI_DOUBLE_PRECISION,1,…,req_send,…)call MPI_IRECV (VEC(12),25,MPI_DOUBLE_PRECISION,1,…,req_recv,…)
endif
if (my_rank.eq.1) thencall MPI_ISEND (VEC( 1),25,MPI_DOUBLE_PRECISION,0,…,req_send,…)call MPI_IRECV (VEC(26),11,MPI_DOUBLE_PRECISION,0,…,req_recv,…)
endif
call MPI_WAITALL (…,req_recv,stat_recv,…)
call MPI_WAITALL (…,req_send,stat_send,…)

これでも良いが，操作が煩雑	
SPMDらしくない	
汎用性が無い	

129

Fundamental MPI

利用例（2）：配列の送受信（3/4）	
if (my_rank.eq.0) then
NEIB= 1start_send=1length_send= 11
start_recv= length_send + 1
length_recv= 25
endif
if (my_rank.eq.1) then
NEIB= 0start_send= 1
length_send= 25
start_recv= length_send + 1
length_recv= 11
endifcall MPI_ISEND&(VEC(start_send),length_send,MPI_DOUBLE_PRECISION,NEIB,…,req_send,…)call MPI_IRECV&(VEC(start_recv),length_recv,MPI_DOUBLE_PRECISION,NEIB,…,req_recv,…)
call MPI_WAITALL (…,req_recv,stat_recv,…)
call MPI_WAITALL (…,req_send,stat_send,…)

一気にSPMDらしくなる	

130

Fundamental MPI

利用例（2）：配列の送受信（4/4）	
if (my_rank.eq.0) then
NEIB= 1start_send=1length_send= 11
start_recv= length_send + 1
length_recv= 25
endif
if (my_rank.eq.1) then
NEIB= 0start_send= 1
length_send= 25
start_recv= length_send + 1
length_recv= 11
endifcall MPI_SENDRECV(VEC(start_send),length_send,MPI_DOUBLE_PRECISION,NEIB,…
VEC(start_recv),length_recv,MPI_DOUBLE_PRECISION,NEIB,…, status,…)

&
&

131

Fundamental MPI

配列の送受信:注意	
#PE0
send:
VEC(start_send)~
VEC(start_send+length_send-1)

#PE1
send:
VEC(start_send)~
VEC(start_send+length_send-1)

#PE0
recv:
VEC(start_recv)~
VEC(start_recv+length_recv-1)

#PE1
recv:
VEC(start_recv)~
VEC(start_recv+length_recv-1)• 送信側の「length_send」と受信側の「length_recv」は一致している必要がある。	
– PE#0⇒PE#1，PE#1⇒PE#0• 「送信バッファ」と「受信バッファ」は別のアドレス	
132

Fundamental MPI

解答例（1/3）
Isend/Irecv/Waitall$> cd <$FVM>/S2$> mpicc –Os -noparallel ex2a.c$> mpif90 –Oss -noparallel ex2a.f$> 実行（2プロセス） go2.sh	
implicit REAL*8 (A-H,O-Z)
include ‘mpif.h’			

integer(kind=4) :: my_rank, PETOT, NEIB	
real (kind=8) :: VEC(36)	
integer(kind=4), dimension(MPI_STATUS_SIZE,1) :: stat_send	
integer(kind=4), dimension(MPI_STATUS_SIZE,1) :: stat_recv	
integer(kind=4), dimension(1) :: request_send	
integer(kind=4), dimension(1) :: request_recv	
integer(kind=4) :: start_send, length_send	
integer(kind=4) :: start_recv, length_recv	
call MPI_INIT(ierr)	
call MPI_COMM_SIZE (MPI_COMM_WORLD, PETOT, ierr )	
call MPI_COMM_RANK (MPI_COMM_WORLD, my_rank, ierr )133Fundamental MPI

解答例（2/3）
Isend/Irecv/Waitall	

if (my_rank.eq.0) then	NEIB= 1	start_send= 1	
length_send= 11	start_recv= length_send + 1	length_recv= 25	
do i= 1, 36
VEC(i)= 100 + i
enddo	
endif	
if (my_rank.eq.1) then	NEIB= 0	start_send= 1	
length_send= 25	start_recv= length_send + 1	length_recv= 11	
do i= 1, 36
VEC(i)= 200 + i
enddo
endif

134

Fundamental MPI

解答例（3/3）
Isend/Irecv/Waitall
	

	
	

	
	

do i= 1, 36	write (*,'(i1,a,i2,f10.0)') my_rank, ' #BEFORE# ', i,VEC(i)	
enddo	
call MPI_ISEND (VEC(start_send), length_send,&	&
MPI_DOUBLE_PRECISION, NEIB, 0, MPI_COMM_WORLD,&
request_send(1), ierr)	
call MPI_IRECV (VEC(start_recv), length_recv,&	&
MPI_DOUBLE_PRECISION, NEIB, 0, MPI_COMM_WORLD,&
request_recv(1), ierr)	

&	&	

call MPI_WAITALL (1, request_recv, stat_recv, ierr)	
call MPI_WAITALL (1, request_send, stat_send, ierr)	
do i= 1, 36	write (*,'(i1,a,i2,f10.0)') my_rank, ' #AFTER # ', i,VEC(i)	
enddo	
call MPI_FINALIZE (ierr)	
end• 汎用性がある	• 「データが全て」という気がして来ないだろうか?
135