2021年度
並列プログラミング入門Ⅱ（MPI）
２０２１年 １０ 月 ８ 日
東北大学サイバーサイエンスセンター
日本電気株式会社

本資料は，東北大学サイバーサイエンスセンターとＮＥＣの共同により作成された．
無断転載等は，ご遠慮下さい．

Page 2

 並列化概要
 OpenMPプログラミング編
 MPIプログラミング編

Page 3

MPIプログラミング編

Page 4MPIプログラミング編・目次
1. MPI概要
2. 演習問題1
3. 演習問題2
4. 演習問題3
5. 演習問題4
6. MPIプログラミング
7. 演習問題58. 演習問題6
付録1.主な手続き
2.参考文献，Webサイト

Page 5

演習問題の構成▌演習問題の環境を自分のホームディレクトリ配下にコピーします。
/mnt/stfs/ap/lecture/MPI/|-- practice_1 演習問題１|-- practice_2 演習問題2|-- practice_3 演習問題3|-- practice_4 演習問題4|-- practice_5 演習問題5|-- practice_6 演習問題6|-- sample
テキスト内のsampleX.fとして掲載しているプログラム|-- etc
その他，テキスト内のetcX.fとして掲載しているプログラム$ cd <環境をコピーしたいディレクトリ>$ cp –r /mnt/stfs/ap/lecture/MPI/ .
Page 6

１．MPI概要▌ 分散メモリ並列処理におけるメッセージパッシングの標準規格
 複数のプロセス間でのデータをやり取りするために用いるメッセージ通信
操作の仕様標準▌ FORTRAN，Cから呼び出すサブプログラムのライブラリ▌ ポータビリティに優れている
 標準化されたライブラリインターフェースによって，様々なMPI実装環境
で同じソースをコンパイル・実行できる▌ プログラマの負担が比較的大きい プログラムを分析して，データ・処理を分割し，通信の内容とタイミング
をユーザが自ら記述する必要がある▌ 大容量のメモリ空間を利用可能
 複数ノードを利用するプログラムの実行により，大きなメモリ空間を利用可能になる(サイバーサイエンスセンターのSX-Aurora TSUBASAでは， 12TByte

まで利用可能)Page 7

MPIの主な機能▌プロセス管理
 MPIプログラムの初期化や終了処理などを行う
一対一通信▌ 一対一通信 一対一で行う通信

プロセス０

プロセス１

プロセス２

プロセス１

プロセス２

▌ 集団通信 グループ内のプロセス全体が関わる通信操作

集団通信

プロセス０

Page 8MPIプログラムの基本構造
a.outのイメージ
PROGRAM MAIN
CALL MPI_INIT(IERR)MPI並列の対象
CALL MPI_FINALIZE(IERR)
STOP
END

実行例
$ mpirun -np 4 ./a.out



MPI_INITがcallされ，
MPI_FINALIZEがcallされるまでの区間がMPI並列の対象



MPI_INITがcallされた時点でプロセスが生成される(mpirunコマン
ドで指定するプロセス数．下の例
ではプロセス数は「4」)

PROGRAM文以降で最初の処理の前(宣言文の後)でMPI_INITをcallするSTOP文の直前にMPI_FINALIZE
をcallする
MPI_INITは付録1.1.4参照
MPI_FINALIZEは付録1.1.5参照

Page 9MPIプログラムの基本構造▌ プログラム実行時のプロセス数を得る
CALL MPI_COMM_SIZE(MPI_COMM_WORLD,NPROCS,IERR)
 mpirunコマンドで指定するプロセス数がNPROCSに返る ループの分割数を決める場合などに使用
 MPI_COMM_WORLDは「コミュニケータ」と呼ばれ，同じ通信の集まりを識別するフラグ
 集団通信は同じコミュニケータを持つ集団間で行う▌ プロセス番号を得る(プロセス番号は0から始まって，プ
ロセス数-1まで)
CALL MPI_COMM_RANK(MPI_COMM_WORLD,MYRANK,IERR)
 自プロセス番号がMYRANKに返る プロセス番号は「ランク」とも呼ばれる
 特定のプロセスでのみ処理を実行する場合などに使用

if(myrank.eq.0) thenwrite(6,*) ”….”endif
Page 10

MPI_COMM_SIZEは付録1.1.7参照
MPI_COMM_RANKは付録1.1.8参照

コンパイル・実行コマンド▌ MPIプログラムのコンパイル
mpinfort [オプション] ソースファイル名※オプションはnfortと同様．

▌MPIプログラムの実行
mpirun –np [総MPIプロセス数] ロードモジュール名Page 11

実行スクリプト例▌32mpi 8smpのジョブを32ノード(32VE)で実行する際のスクリプト例
NQSVオプション(#PBSで指定)#!/bin/csh#PBS -q sx#PBS --venode 32#PBS -l elapstim_req=20:00:00#PBS –N Test_Job#PBS -v OMP_NUM_THREADS=8-q
キュー名を指定（必須）--venode 使用VE数を指定（必須）-l
経過時間(hh:mm:ss)の申告（強く推奨）-N
ジョブ名を指定（任意）-v (実行する全てのノードに対して)環境変数を設定する

cd $PBS_O_WORKDIROMP_NUM_THREADS=8を実行する全てのノードに対して設定する

mpirun -np 32 ./a.out$PBS_O_WORKDIR：ジョブスクリプトをqsubしたディレクトリ▌ SX-Aurora TSUBASAのジョブクラス
利用形態

キュー名

VE数

実行形態（※）

最大経過時間
既定値/最大値

メモリサイズ

無料

sxf

1

1VE

1時間/1時間

48GB×VE数

共有

sx

1

1VE

72時間/720時間

2～256

8VE単位で確保(VHを共用しない)

2～8

1VE単位で確保(VHを共用する)

sxmix

Page 12

詳細は https://www.ss.cc.tohoku.ac.jp/sscc /wp-content/uploads/pdf/ジョブの実行方法.pdf を参照

MPIプログラム例(Hello World)
program sample1
write(6,*) "Hello World"
stop
end

逐次プログラム

sample1.fsample2.f
MPIプログラム
program sample2
include 'mpif.h'
integer ierr,myrank,nprocs
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)write(6,*) “Hello World My rank=”,myrank,“(”,nprocs,“processes)”call MPI_FINALIZE(ierr)stop
end$ mpirun -np 4 ./a.out
Hello World My rank=
Hello World My rank=
Hello World My rank=
Hello World My rank=

Page 13

3(
2(
0(
1(

4 processes)
4 processes)
4 processes)
4 processes)



MPIのインクルードファイル
を指定する
MPIの初期化(MPI_INIT)

MPIの終了化(MPI_FINALIZE)

4プロセスで実行「Hello World」とランク番
号,プロセス数が4回出力

MPIプログラムの動作
ランク０

ランク１

ランク２

ランク３

MPI_INIT

write(6,*)”Hello
World”,myrank

write(6,*) ”Hello
World”,myrank

write(6,*) “Hello
World”,myrank

write(6,*) “Hello
World”,myrank

MPI_FINALIZE① mpirunコマンドを実行(-npサブオプションのプロセス数は4)② ランク0のプロセスが生成③ MPI_INITをcallする時点でランク1,2,3のプロセスが生成④ 各ランクで「write(6,*)“Hello World”,myrank」が実行⑤ 出力する順番はタイミングで決まる(ランク番号順ではない)

Page 14

２．演習問題1▌(1-1)P13 のプログラム(sample2.f)をコンパイル，実行してください▌(1-2)P13 のMPIプログラム「Hello World」の結果をラン
ク0のみが出力するように書き換えてください

Page 15

演習問題1-1（practice_1)▌P13のソースファイル(sample2.f)をpractice1.f としてコピーし，コンパ
イル・実行してください
項目

対象

備考

作業ディレクトリ

practice_1

使用ソースファイル

practice1.f(sample2.fをコピー)

編集 不要

ジョブファイル

run.sh

そのまま投入 手順①：作業ディレクトリを移動して，ファイルをコピーしてください．
% cd MPI/practice_1% cp ../sample/sample2.f practice1.f

 手順②：コンパイルしてください．
% mpinfort practice1.f

Page 16

演習問題1-1（practice_1)つづき 手順③：実行スクリプトを確認します．
% cat run.sh#!/bin/csh#PBS -q sx#PBS --venode 1#PBS -N practice_p1
cd $PBS_O_WORKDIR
mpirun -np 8 ./a.out

VEノード数：1
MPIプロセス数：8
ジョブクラス：sx
ジョブ名：practice_p1
実行ディレクトリ：カレントディ
レクトリ

 手順④：ジョブを投入します．
% qsub run.sh 手順⑤：実行結果を確認します．結果はpractice_p1.oXXXX(XXXXにはリクエストIDが入ります)として格納されます．
% cat practice_p1.oXXXX

Page 17

演習問題1-2（practice_1)▌演習問題1-1で使ったMPIプログラム「Hello World」の結果をランク0のみが出力するように書き換えてください
項目

対象

備考

作業ディレクトリ

practice_1

使用ソースファイル

practice1-2.f

編集 必要

ジョブファイル

run.sh

そのまま投入 手順①：エディタでソースファイルを編集してください．
% vi practice1-2.f (vi以外のエディタでも構いません)※次ページにプログラム編集のヒントがあります．

Page 18

演習問題1-2（practice_1)

▌ヒント
以下の

の部分を埋めてください．（P13を参考にしてください）

program sample1_2
include 'mpif.h'
integer ierr,myrank,nprocs
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)if(myrank.eq.0) write(6,*) "Hello World",myrankcall MPI_FINALIZE(ierr)
stop
end

Page 19

 MPIのインクルードファイルを指定MPIの初期化プログラム実行時のプロセス数の取得
プロセス番号の取得
IF文で，自分自身がランク0である場合に出力を行うようにします．

 MPIの終了

演習問題1-2（practice_1) つづき 手順②：コンパイルを実行します．
% mpinfort practice1-2.f

 手順③：ジョブを投入します．
% qsub run.sh 手順④：実行結果を確認します．結果はpractice_p1.oXXXX(XXXXにはリクエストIDが入ります)として格納されます．
% cat practice_p1.oXXXX

Page 20

総和プログラムのMPI化▌ 1から1000の総和を求める(逐次実行プログラム)
sample3.f
program sample3
parameter(n=1000)
integer isum
isum=0do i=1,nisum=isum+i
enddo
write(6,*) "Total = ",isum
stop
end

Page 21

総和計算

結果出力

総和プログラムのMPI化▌ 逐次プログラム処理イメージ• 総和計算部分は，DOループ• 結果出力は，write文- 最後の１回だけ
i=１～１０００の和を取る• 処理時間が一番大きいDOループが並
列処理のターゲット
結果出力

Page 22

総和プログラムのMPI化▌ 4分割の処理イメージ

i=1,250で和を取る

i=251,500
で和を取る
i=501,750
で和を取る
i=751,1000
で和を取る
結果出力

Page 23

i=1,1000までの和を取る処理は，
i= 1, 250までの和を取る処理
i=251, 500までの和を取る処理
i=501, 750までの和を取る処理
i=751,1000までの和を取る処理に分割することができる．
しかも順不同．

総和プログラムのMPI化▌ 並列処理のイメージ(4分割)
逐次処理

ランク０

ランク１

ランク２

ランク３

分割

i=1
～
i=250

i=251
i=1
～
～
i=500
i=25

i=501
～
i=750

i=751
～
i=1000

部分和の計算i=1～1000の和結果出力

結果出力• 分割は，全分割数と自プロセス番号で求める• 集計処理の方法• 結果出力は，１プロセスのみ

Page 24

集計

総和プログラムのMPI化▌ 分割の方法 (n=1000の場合)

 始点の求め方－ ((n-1)/nprocs+1)* myrank+1
 終点の求め方－ ((n-1)/nprocs+1)*(myrank+1)
但し，全分割数はnprocs,自プロセス番号はmyrank
本例は，ｎがプロセス数で割り切れることを前提としている

数値例

Page 25

nprocs=4

始点

終点

myrank=0

1

250

myrank=1

251

500

myrank=2

501

750

myrank=3

751

1000

３．演習問題2 (practice_2)▌1から1000の総和を8分割してMPI並列で実行し，部分和を各ランクから出力してください ヒント：プログラムの流れは下記のとおり
ＭＰＩの初期化処理
プロセス数と自プロセスのランク番号の取得
分割時の始点と終点を求める
部分和に初期値(=0)を与える
部分和を求めるループの実行
部分和の出力ＭＰＩの終了化処理

Page 26

演習問題2 (practice_2)つづき
項目

対象

備考

作業ディレクトリ

practice_2

使用ソースファイル

practice2.f

編集 必要

ジョブファイル

run.sh

そのまま投入 手順①：作業ディレクトリを移動してください．
% cd MPI/practice_2

 手順②：エディタでソースファイルを編集してください．ソースファイ
ルpractice2.fは ディレクトリ practice_2/ に用意しています．
% vi practice2.f※次ページにプログラム編集のヒントがあります．

Page 27

演習問題2 (practice_2)つづき
▌ヒント
以下の

の部分を埋めてください．

program sample2
include 'mpif.h'
integer ierr,myrank,nprocs,ist,ied
parameter(n=1000)
integer isumcall MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
ist=((n-1)/nprocs+1)* myrank+1
ied=((n-1)/nprocs+1)*(myrank+1)
isum=0do i=ist,ied
isum=isum+i
enddo
write(6,6000) myrank,isum6000 format("Total of Rank:",i2,i10)
call MPI_FINALIZE(ierr)
stop
end

Page 28 始点の計算式を入れてください 終点の計算式を入れてください演習問題2 (practice_2)つづき 手順③：コンパイルを実行します．
% mpinfort practice2.f

 手順④：ジョブを投入します．
% qsub run.sh 手順⑤：実行結果を確認します．結果はpractice_p2.oXXXX(XXXXにはリクエストIDが入ります)として格納されます．
% cat practice_p2.oXXXX

Page 29

MPIデータ転送▌各プロセスは独立したプログラムと考える
 各プロセスは独立したメモリ空間を有する
 他のプロセスのデータを直接アクセスすることは不可
 データ転送により他のプロセスのデータをアクセスすることが可能▌MPI_SEND/MPI_RECV
 同期型の1対1通信
 特定のプロセス間でデータの送受信を行う．データ転送が完了するまで処理は中断

Page 30

MPI_SEND/MPI_RECV
ランク０の配列の一部部分をランク１へ転送
ランク０

ランク１

配列

配列

MPI_SEND

転送処理

MPI_RECV

転送が完了した後は配列への定義・参照が可能

Page 31

転送が完了するまで処理はブロックされる

MPI_SEND/MPI_RECV
sample4.f

program sample4
include 'mpif.h'
integer nprocs,myrank,itag,ierrinteger status(MPI_STATUS_SIZE)
real work(10)call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)itag=1work=0.0
if(myrank.eq.0) thendo i=1,10
work(i)=float(i)enddocall MPI_SEND(work(4),3,MPI_REAL,1,itag,MPI_COMM_WORLD,ierr)else if(myrank.eq.1) thencall MPI_RECV(work(4),3,MPI_REAL,0,itag,MPI_COMM_WORLD,+
status,ierr)write(6,*) work
endif
call MPI_FINALIZE(ierr)
stop
end

Page 32

詳細は付録1.2.3
詳細は付録1.2.5

４．演習問題3 (practice_3)▌演習問題2のプログラムの各ランクの部分和をランク0に集めて，総和を計算し出力してください ヒント：転送処理は以下
ランク1,2,3(0以外)call MPI_SEND(isum,1,MPI_INTEGER,0,&
itag,MPI_COMM_WORLD,ierr)

ランク0
call MPI_RECV(isum2,1,MPI_INTEGER,1,&
itag,MPI_COMM_WORLD,status,ierr)call MPI_RECV(isum2,1,MPI_INTEGER,2,&
itag,MPI_COMM_WORLD,status,ierr)call MPI_RECV(isum2,1,MPI_INTEGER,3,&
itag,MPI_COMM_WORLD,status,ierr)※isumで受信するとランク0の部分和が上書きされてしまう

Page 33

４．演習問題3 (practice_3)つづき
項目

対象

備考

作業ディレクトリ

practice_3

使用ソースファイル

practice3.f

編集 必要

ジョブファイル

run.sh

そのまま投入 手順①：作業ディレクトリを移動してください．
% cd MPI/practice_3

 手順②：エディタでソースファイルを編集してください．演習問題2の回答例としてpractice3.fをディレクトリ practice_3/ に用意しています．ご自身が作成された演習問題2の回答を使用したい場合は，
practice_2/からpractice2.fをコピーして使用してください．
% vi practice3.f※次ページにプログラム編集のヒントがあります．

Page 34

４．演習問題3 (practice_3)つづき
▌ヒント
以下の

の部分を埋めてください．（P33を参考にしてください）

program example3
include 'mpif.h'
integer ierr,myrank,nprocs,ist,ied,itag
integer status(MPI_STATUS_SIZE)
parameter(n=1000)
integer isum,isum2
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
ist=((n-1)/nprocs+1)*myrank+1
ied=((n-1)/nprocs+1)*(myrank+1)
isum=0do i=ist,ied
isum=isum+i
enddo
itag=1if(myrank.ne.0) thencall MPI_SEND(isum,1,MPI_INTEGER,0,&
itag,MPI_COMM_WORLD,ierr)else
call MPI_RECV(isum2,1,MPI_INTEGER,1,&
itag,MPI_COMM_WORLD,status,ierr)isum=isum+isum2
call MPI_RECV(isum2,1,MPI_INTEGER,2,&
itag,MPI_COMM_WORLD,status,ierr)isum=isum+isum2
call MPI_RECV(isum2,1,MPI_INTEGER,3,&
itag,MPI_COMM_WORLD,status,ierr)isum=isum+isum2
write(6,6000) isum6000 format("Total Sum = ",i10)
endifcall MPI_FINALIZE(ierr)
stop
end

Page 35

 ランク0以外であれば，ランク0に送信します．

 ランク0は，ランク1～3からデータを受信します．
 総和を出力します．

４．演習問題3 (practice_3)つづき 手順③：コンパイルを実行します．
% mpinfort practice3.f

 手順④：ジョブを投入します．
% qsub run.sh 手順⑤：実行結果を確認します．結果はpractice_p3.oXXXX(XXXXにはリクエストIDが入ります)として格納されます．
% cat practice_p3.oXXXX

Page 36

MPI集団通信▌ あるプロセスから同じコミュニケータを持つ全プロセスに対して同時に通信を行う▌ または同じコミュニケータを持つプロセス間でデータを共有する(例)代表プロセスのデータを同じコミュニケータを持つ全プ
ロセスへ送信する
CALL MPI_BCAST(DATA,N,MPI_REAL,0,MPI_COMM_WORLD,IERR) N個の実数型データを格納するDATAをランク0 から送信
 コミュニケータMPI_COMM_WORLDを持つ全プロセスに送信される
 MPI_BCASTがcallされる時に同期処理が発生(通信に参加する全プロ
セスの足並みを揃える)
詳細は付録1.3.6
ランク0

Page 37

ランク1

ランク2

ランク3

・・・・

ランクN

MPI_REDUCE▌ 同じコミュニケータを持つプロセス間で総和，最大，最少などの演算を行い，結果を代表プロセスに返す
sample5.f

program sample5
include 'mpif.h'
integer myrank,nprocs,isum,ierrcall MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)call MPI_REDUCE(myrank,isum,1,MPI_INTEGER,MPI_SUM,0,+
MPI_COMM_WORLD,ierr)if(myrank.eq.0) write(6,*)"Result = ",isum
call MPI_FINALIZE(ierr)
stop
end

$ mpirun -np 4 ./a.out
Result =6

Page 38

コミュニケータ
MPI_COMM_WORLDを持つプ
ロセスのランク番号の合計をランク0に集計して出力する

MPI_REDUCEの詳細は付録1.3.3

MPI_SCATTER▌ 同じコミュニケータを持つプロセス内の代表プロセスの送信バッファ
から，全プロセスの受信バッファにメッセージを送信する．
▌ 各プロセスへのメッセージ長は一定である．
代表プロセスcall MPI_SCATTER(senddata,icount,MPI_INTEGER,&
recvdata(icount*myrank+1),icount,&
MPI_INTEGER,0,MPI_COMM_WORLD,ierr) 送信バッファと受信バッファはメモリ上の重なりがあってはならない(MPI1.0仕様)
 各プロセスへのメッセージ長が一定でない場合はMPI_SCATTERVを使用する．
MPI_SCATTERの詳細は付録1.3.13
MPI_SCATTERVの詳細は付録1.3.14

Page 39

MPI_GATHER▌ 同じコミュニケータを持つ全プロセスの送信バッファから，代表プロセ
スの受信バッファにメッセージを送信する．
▌ 各プロセスからのメッセージ長は一定である．

代表プロセスcall MPI_GATHER(senddata(icount*myrank+1),&
icount,MPI_INTEGER,recvdata,&
icount,MPI_INTEGER,0,MPI_COMM_WORLD,&
ierr)
 送信バッファと受信バッファはメモリ上の重なりがあってはならない(MPI1.0仕様)
 各プロセスへのメッセージ長が一定でない場合はMPI_GATHERVを使用する．
MPI_GATHERの詳細は付録1.3.8
MPI_GATHERVの詳細は付録1.3.9

Page 40

５．演習問題4 (practice_4)▌ 演習問題3のプログラムで，各ランクの部分和をMPI_REDUCEを使用してランク0に集計して，ランク0から結果を出力してください
項目

対象

備考

作業ディレクトリ

practice_4

使用ソースファイル

practice4.f

編集 必要

ジョブファイル

run.sh

そのまま投入 手順①：作業ディレクトリを移動してください．
% cd MPI/practice_4

 手順②：エディタでソースファイルを編集してください．演習問題3の回答例としてpractice4.fをディレクトリ practice_4/ に用意しています．ご自身が作成された演習問題3の回答を使用したい場合は，
practice_3/からpractice3.fをコピーして使用してください．
% vi practice4.f※次ページにプログラム編集のヒントがあります．
Page 41

５．演習問題4 (practice_4) つづき
▌ヒント
以下の

の部分を埋めてください．

program example4
include 'mpif.h'
integer ierr,myrank,nprocs,ist,ied
parameter(n=1000)
integer isum,isum2
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
ist=((n-1)/nprocs+1)*myrank+1
ied=((n-1)/nprocs+1)*(myrank+1)
isum=0do i=ist,ied
isum=isum+i
enddo
call MPI_REDUCE(isum,isum2,1,MPI_INTEGER,MPI_SUM,0,&
MPI_COMM_WORLD,ierr)if(myrank.eq.0) write(6,6000) isum2
6000 format("Total Sum = ",i10)
call MPI_FINALIZE(ierr)
stop
end

Page 42 送信データのアドレス
 受信データのアドレス
 送信データの要素数
 リダクション演算の機能コード
 rootプロセスのランク
 ランク0が集計結果を出力させる
ようにします．

５．演習問題4 (practice_4) つづき 手順③：コンパイルを実行します．
% mpinfort practice4.f

 手順④：ジョブを投入します．
% qsub run.sh 手順⑤：実行結果を確認します．結果はpractice_p4.oXXXX(XXXXにはリクエストIDが入ります)として格納されます．
% cat practice_p4.oXXXX

Page 43

６．MPIプログラミング▍ 通信の発生▍ デッドロック▍ 配列の縮小▍ ファイルの入出力▍ MPI＋OpenMPのハイブリッド実行

Page 44

通信の発生▌袖領域
配列を3つの配列に分割すると，
CPU0

CPU1

分割

しかし，
他のCPUが保持するデータを参照する仕組みが必要
データの送受信CPU0

必要なデータが足らない
必要なデータが足らない
Page 45

CPU１

CPU2

CPU2

境界を跨ぐ例▌対象のDOループに含まれる配列の添え字がi+1やi-1の場
合，ループを分割した時にできる境界を跨ぐ
逐次版
do i=1, 100b(i)=a(i)-a(i-1)
enddo

並列版
プロセス０

プロセス１

プロセス２

Page 46

1

…

25

25

26

…

50

50

51

…

75

不足データの送受信▌分割境界におけるデータを補うには，メッセージ交換によるデータの送受信が必要
メッセージ
交換

プロセス０

プロセス１

ist

ied

ist

プロセス２

ist
メッセージ
交換

Page 47

ied

ied

領域分割時のメッセージ交換
MPI版：
ist = ((100-1)/nprocs+1)*myrank+1
ied = ((100-1)/nprocs+1)*(myrank+1)
iLF = myrank-1
iRT = myrank+1
if (myrank.ne.0) thencall mpi_recv(a(ist-1),1,MPI_REAL8,iLF,1, &MPI_COMM_WORLD,status,ierr)
endif
do i= ist, iedb(i) = a(i) – a(i-1)
enddo
if (myrank.ne.nprocs-1) thencall mpi_send(a(ied),1,MPI_REAL8,iRT,1, &MPI_COMM_WORLD,ierr)
endif：

Page 48

担当領域の算出
送受信相手の特定

デッドロック
if(myrank.eq.0) thencall MPI_Recv(rdata,1,MPI_REAL,1,+
itag,MPI_COMM_WORLD,status,ierr)else if(myrank.eq.1) thencall MPI_Recv(rdata,1,MPI_REAL,0,+
itag,MPI_COMM_WORLD,status,ierr)endif
if(myrank.eq.0) thencall MPI_Send(sdata,1,MPI_REAL,1,+
itag,MPI_COMM_WORLD,ierr)else if(myrank.eq.1) thencall MPI_Send(sdata,1,MPI_REAL,0,+
itag,MPI_COMM_WORLD,ierr)endif• ランク0とランク1が同時にMPI_RECV(同期型1対1通信)を行うと，送受信が完了せず，待ち状態となる．
• このような待ち状態をデッドロックという．
Page 49

デッドロック
ランク０
MPI_RECV

ランク１

×

MPI_RECV※ランク０とランク１から同時にMPI_RECVを実行するとデータが送信されるのを待つ状態で止まってしまう．

MPI_SENDMPI_SEND
デッドロック発生• デッドロックの回避方法としては，以下が挙げられる① MPI_RECVとMPI_SENDの正しい呼び出し順序に修正② 非同期型にMPI_IRECVとMPI_ISENDに置き換える

①

ランク０

ランク１

MPI_RECVMPI_SENDMPI_SENDMPI_RECV

Page 50

②

ランク０

ランク１

MPI_IRECVMPI_IRECVMPI_ISEND

MPI_ISENDMPI_WAITMPI_WAIT

デッドロックの回避①
if(myrank.eq.0) thencall MPI_Recv(rdata,1,MPI_REAL,1,+
itag,MPI_COMM_WORLD,status,ierr)else if(myrank.eq.1) thencall MPI_Send(sdata,1,MPI_REAL,0,+
itag,MPI_COMM_WORLD,ierr)endif
if(myrank.eq.0) thencall MPI_Send(sdata,1,MPI_REAL,1,+
itag,MPI_COMM_WORLD,ierr)else if(myrank.eq.1) thencall MPI_Recv(rdata,1,MPI_REAL,0,+
itag,MPI_COMM_WORLD,status,ierr)
endif

 MPI_SENDとMPI_RECVが対になるように呼び出し順序を変更

Page 51

デッドロックの回避②
if(myrank.eq.0) thencall MPI_IRecv(rdata,1,MPI_REAL,1,+
itag,MPI_COMM_WORLD,ireq1,ierr)else if(myrank.eq.1) thencall MPI_IRecv(rdata,1,MPI_REAL,0,+
itag,MPI_COMM_WORLD,ireq1,ierr)endif
if(myrank.eq.0) thencall MPI_ISend(sdata,1,MPI_REAL,1,+
itag,MPI_COMM_WORLD,ireq2,ierr)else if(myrank.eq.1) thencall MPI_ISend(sdata,1,MPI_REAL,0,+
itag,MPI_COMM_WORLD,ireq2,ierr)endif
call MPI_WAIT(ireq1,status,ierr)call MPI_WAIT(ireq2,status,ierr) 非同期型のMPI_ISENDとMPI_IRECVに置き換える
MPI_ISENDの詳細は付録1.2.7

MPI_IRECVの詳細は付録1.2.8
MPI_WAITの詳細は付録1.2.10

Page 52配列の縮小配列a(100)… 各プロセスが担当する領域
各プロセスは，全体の配列を持つ必要がない

メモリ領域の節約ができる

Page 53

縮小イメージ（内積）
プロセス０

プロセス１

プロセス２

プロセス３

real(8)::a(100)do i=1,25c=c+a(i)*b(i)enddoreal(8)::a(100)do i=26,50
c=c+a(i)*b(i)enddoreal(8)::a(100)do i=51,75
c=c+a(i)*b(i)enddoreal(8)::a(100)do i=76,100
c=c+a(i)*b(i)enddoreal(8)::a(25)do i=1,25c=c+a(i)*b(i)enddoreal(8)::a(25)do i=1,25c=c+a(i)*b(i)enddoreal(8)::a(25)do i=1,25c=c+a(i)*b(i)enddoreal(8)::a(25)do i=1,25c=c*a(i)*b(i)
enddo

プロセス０

Page 54

プロセス１

プロセス２

プロセス３

ファイル入出力▌ MPIによって並列化されたプログラムのファイル入出力には幾つかのパターンがあり，それぞれに特徴があるため，実際のプログラム例を記載
する．
1. ファイル入力① 全プロセス同一ファイル入力
 逐次プログラムから移行し易い② 代表プロセス入力

 メモリの削減が可能③ 分散ファイル入力

 メモリ削減に加え，Ｉ／Ｏ時間の削減が可能

2. ファイル出力① 代表プロセス出力
 ファイルを１つにまとめる② 分散ファイル出力

 Ｉ／Ｏ時間の削減が可能

Page 55

全プロセス同一ファイル入力
etc1.f

ランク０

ランク１

ランク２

ランク３

：I/O

Page 56

include 'mpif.h'
integer,parameter::numdat=100
integer::idat(numdat)
integer::myrank,nprocs,ist,ied,isum,ierrcall MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
ist=((numdat-1)/nprocs+1)*myrank+1
ied=((numdat-1)/nprocs+1)*(myrank+1)
open(10,file='fort.10')
read(10,*) idat
isum=0do i=ist,ied
isum=isum+idat(i)enddowrite(6,*) myrank,':partial sum=',isum
call MPI_FINALIZE(ierr)
stop
end

代表プロセス入力
etc2.f

ランク０
ランク１

ランク２

ランク３

：MPI通信：I/OPage 57

include 'mpif.h'
integer,parameter :: numdat=100
integer::senddata(numdat),recvdata(numdat)
integer::myrank,nprocs,icount,isum,ierrcall MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)if(myrank.eq.0)thenopen(10,file='fort.10')
read(10,*) senddata
endif
icount=(numdat-1)/nprocs+1
call MPI_SCATTER(senddata,icount,MPI_INTEGER,&
recvdata(icount*myrank+1),icount,&
MPI_INTEGER,0,MPI_COMM_WORLD,ierr)isum=0do i=1,icountisum=isum+recvdata(icount*myrank+i)
enddo
write(6,*)myrank,':partial sum=',isum
call MPI_FINALIZE(ierr)
stop
MPI_SCATTERの詳細は付録1.3.13
end

代表プロセス入力＋メモリ削減

ランク０

ランク０
ランク１

ランク２

ランク３

：MPI通信：I/OPage 58

include 'mpif.h'
integer,parameter :: numdat=100
integer,allocatable :: idat(:),work(:)
integer :: nprocs,myrank,ierrinteger :: ist,ied,isum
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
ist = ((numdat-1)/nprocs+1)*myrank+1
ied = ((numdat-1)/nprocs+1)*(myrank+1)
allocate(idat(ist:ied))
if(myrank.eq.0) thenallocate(work(numdat))
open(10,file='fort.10')
read(10,*) work
endif
call MPI_SCATTER(work,ied-ist+1,MPI_INTEGER,+
idat(ist),ied-ist+1,MPI_INTEGER,0,+
MPI_COMM_WORLD,ierr)if(myrank.eq.0) deallocate(work)
isum=0do i=ist,ied
isum = isum + idat(i)enddowrite(6,*) myrank,';partial sum=',isum
deallocate(idat)
call MPI_FINALIZE(ierr)
stop
end

分散ファイル入力
include 'mpif.h'
integer,parameter :: numdat=100
integer::buf(numdat)
integer :: myrank,nprocs,ist,ied,isum,ierr
ランク０

ランク１

c
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)cist=((numdat-1)/nprocs+1)*myrank+1
ied=((numdat-1)/nprocs+1)*(myrank+1)
read(10+myrank,*) (buf(i),i=ist,ied)

ランク２cisum=0do i=ist,ied
isum = isum + buf(i)
enddo

ランク３

c
：I/O

Page 59

etc3.fwrite(6,*) myrank,';partial sum=',isum
call MPI_FINALIZE(ierr)
stop
end

代表プロセス出力
etc4.f

ランク３

ランク２

ランク１

ランク０

：MPI通信：I/OPage 60

include 'mpif.h'parameter (numdat=100)
integer senddata(numdat),recvdata(numdat)
integer myrank,nprocs,icount,ierrcall MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)icount=(numdat-1)/nprocs+1
do i=1,icountsenddata(icount*myrank+i)=icount*myrank+i
enddo
call MPI_GATHER(senddata(icount*myrank+1),&
icount,MPI_INTEGER,recvdata,&
icount,MPI_INTEGER,0,MPI_COMM_WORLD,&
ierr)
if(myrank.eq.0)thenopen(60,file='fort.60')
write(60,'(10I8)') recvdata
endif
call MPI_FINALIZE(ierr)
stop
MPI_GATHERの詳細は付録1.3.8
end分散ファイル出力

etc5.f

ランク０

ランク１

ランク２

ランク３

：I/O

Page 61

include 'mpif.h'
integer,parameter :: numdat=100
integer :: buf(numdat)
integer :: myrank,nprocs,ist,ied,ierrcall MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
ist=((numdat-1)/nprocs+1)*myrank+1
ied=((numdat-1)/nprocs+1)*(myrank+1)
do i=ist,ied
buf(i)=i
enddo
write(60+myrank,'(10I8)') (buf(i),i=ist,ied)call MPI_FINALIZE(ierr)
stop
end

７．演習問題５▌ P60のetc4.fをP58の「代表プロセス入力＋メモリ削減」の例のように，各プロセスに必要な領域だけ確保するように修正してください．

Page 62

７．演習問題5 (practice_5) つづき
項目

対象

備考

作業ディレクトリ

practice_5

使用ソースファイル

practice5.f

編集 必要

ジョブファイル

run.sh

そのまま投入 手順①：作業ディレクトリを移動してください．
% cd MPI/practice_5

 手順②：エディタでソースファイルを編集してください．ソースファイ
ルpractice5.fは practice_5/ に用意しています．
% vi practice5.f※次ページにプログラム編集のヒントがあります．

Page 63

７．演習問題5 (practice_5) つづき
▍ヒント
以下の

の部分を埋めてください．

include 'mpif.h'
integer,parameter :: numdat=100
integer,allocatable :: senddata(:),recvdata(:)
integer :: myrank,nprocs,icount,ierrcall MPI_INIT(ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)
ist = ((numdat-1)/nprocs+1)*myrank+1
ied = ((numdat-1)/nprocs+1)*(myrank+1)
 必要な分だけallocate
allocate(senddata(ist:ied))
します．
if(myrank.eq.0) allocate(recvdata(numdat))icount=(numdat-1)/nprocs+1
do i=1,icountsenddata(icount*myrank+i)=icount*myrank+i
enddo

次ページへつづく．
Page 64

７．演習問題5 (practice_5) つづき 送信データの開始アドレス
 送信データの要素数

call MPI_GATHER(senddata(icount*myrank+1),&
icount,MPI_INTEGER,recvdata,&
icount,MPI_INTEGER,0,MPI_COMM_WORLD,&
ierr)
if(myrank.eq.0) thenopen(60,file='fort.60')
write(60,'(10I8)') recvdata
deallocate(recvdata)
endif
deallocate(senddata)
call MPI_FINALIZE(ierr)
stop
end

Page 65

 受信領域の開始アドレス

 個々のプロセスから受信する要素数

 最後にdeallocateします．

７．演習問題5 (practice_5) つづき 手順③：コンパイルを実行します．
% mpinfort practice5.f

 手順④：ジョブを投入します．
% qsub run.sh 手順⑤：実行結果を確認します．結果はpractice_p5.oXXXX(XXXXにはリクエストIDが入ります)として格納されます．
% cat practice_p5.oXXXX

Page 66

MPI＋OpenMPのハイブリッド実行 (1/2)▍ MPI(Message Passing Interface)とOpenMPを組み合わせることにより，複数ノード(複数VE)のSX-Aurora TSUBASAが利用可能▍ VE間をMPI並列(最大256VE)，VE内をOpenMP並列(最大8スレッ
ド)▍ ハイブリッド実行のメリットは以下① 並列数の増加による処理時間の短縮② MPI転送ネックのプログラムにおいて，転送回数の削減(コードによっては転送量の削減も)Page 67

MPI＋OpenMPのハイブリッド実行 (2/2)▍ コンパイルはmpinfortコマンドを利用
mpinfort -fopenmp [オプション] ソースファイル名▍ ハイブリッド実行のスクリプトの例#!/bin/csh#PBS -q ①#PBS --venode ②#PBS -l elapstim_req=③#PBS -v OMP_NUM_THREADS=④
cd $PBS_O_WORKDIR
mpirun -np ⑤ ./a.out※詳細は「AOBA-A プログラム開発・実行環境① キュー名を指定（必須）② 使用VE数を指定．（必須）③ 使用計算時間（経過時間）を設定．
「hh:mm:ss」のように指定．（強く推奨）④ スレッド数を指定．（共有並列の場合必須）⑤ 総MPIプロセス数を指定．
（VE内がすべて共有メモリ並列の場合は，②=⑤）

利用手順書」を参照

https://www.ss.cc.tohoku.ac.jp/sscc /wp-content/uploads/pdf/AOBA-Aプログラム開発・実行環境利用手順書.pdf

Page 68

８．演習問題6(practice_6)▌行列積プログラムをMPIで並列化してください
implicit real(8)(a-h,o-z)
parameter ( n=12000 )real(8) a(n,n),b(n,n),c(n,n)
real(4) etime,cp1(2),cp2(2),t1,t2,t3
do j = 1,ndo i = 1,na(i,j) = 0.0d0b(i,j) = n+1-max(i,j)
c(i,j) = n+1-max(i,j)
enddo
enddo
write(6,50) ' Matrix Size = ',n50 format(1x,a,i5)
t1=etime(cp1)
do j=1,ndo k=1,ndo i=1,na(i,j)=a(i,j)+b(i,k)*c(k,j)
end doend doend dot2=etime(cp2)t3=cp2(1)-cp1(1)write(6,60) ' Execution Time = ',t2,' sec',' A(n,n) = ',a(n,n)
60 format(1x,a,f10.3,a,1x,a,d24.15)
stop
end
Page 69

 左記に記載する行列積を行うプロ
グラムをMPI化して8プロセスで実行してください．出力はプロセ
ス０で行ってください．

８．演習問題6(practice_6)つづき ヒント：プログラムの流れは下記のとおり
ＭＰＩの初期化処理
プロセス数と自プロセスのランク番号の取得

 時間計測はMPI_Wtimeを使用する

分割時の始点と終点を求める①時間を格納する変数はreal*8で定義す
る

解を格納する配列aの初期化行列bとcの値の設定

real*8 t1,t2②測定する区間の始まりと終わりの時間を計測する

各プロセスが担当する範囲の行列積を計算

解を格納する配列aをランク０に集める
ランク０が結果を出力

ＭＰＩの終了化処理

Page 70

call MPI_BARRIER(MPI_COMM_WORLD,IERR)
t1=MPI_WTIME()[測定区間]

call MPI_BARRIER(MPI_COMM_WORLD,IERR)
t2=MPI_WTIME()③t2-t1が計測区間の時間となる

８．演習問題6(practice_6)つづき データの転送方法（行列－ベクトル積） プロセス0はプロセス1,2,3から計算結果を格納した配列ｘを受け取る
（下図）

プロセス０

プロセス１

プロセス２




Page 71

プロセス３

MPI_GATHERを使用して，各ランクに分散したデー
タを集める
詳細は並列プログラミング入門テキストの付録1.3.8

８．演習問題6 (practice_6) つづき
項目

対象

備考

作業ディレクトリ

practice_6

使用ソースファイル

practice6.f

編集 必要

ジョブファイル

run.sh

そのまま投入 手順①：作業ディレクトリを移動してください．
% cd MPI/practice_6

 手順②：エディタでソースファイルを編集してください．逐次版の行列積プログラムのソースファイルpractice6.fは practice_6/ に用意しています．
% vi practice6.f※次ページにプログラム編集のヒントがあります．

Page 72

８．演習問題6 (practice_6) つづき
▍ヒント
以下の

の部分を埋めてください．

program example6
implicit real(8)(a-h,o-z)
include 'mpif.h'
integer ierr,myrank,nprocs,ist,ied
parameter ( n=12000 )real(8) a(n,n),b(n,n),c(n,n)
real(8) d(n,n)
real(8) t1,t2
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
ist=((n-1)/nprocs+1)*myrank+1
ied=((n-1)/nprocs+1)*(myrank+1)
n2=n/nprocs

次ページへつづく．
Page 73

 分割点の始点と終点を決めます．

８．演習問題6 (practice_6) つづき
do j = 1,ndo i = 1,na(i,j) = 0.0d0b(i,j) = n+1-max(i,j)
c(i,j) = n+1-max(i,j)
enddo
enddo
if(myrank.eq.0) thenwrite(6,50) ' Matrix Size = ',nendif
50 format(1x,a,i5)
call MPI_BARRIER(MPI_COMM_WORLD,ierr)
t1=MPI_WTIME()

次ページへつづく．

Page 74

 バリア処理 時間計測区間の開始

８．演習問題6 (practice_6) つづき
do j=ist,ied
do k=1,ndo i=1,na(i,j)=a(i,j)+b(i,k)*c(k,j)
end doend doend docall MPI_GATHER(a(1,ist),n*n2,MPI_REAL8,d,n*n2&,MPI_REAL8,0,MPI_COMM_WORLD, ierr)call MPI_BARRIER(MPI_COMM_WORLD,ierr)
t2=MPI_WTIME()
if(myrank.eq.0) thenwrite(6,60) ' Execution Time = ',t2-t1,' sec',' A(n,n) = ',d(n,n)
endif
60 format(1x,a,f10.3,a,1x,a,d24.15)
call MPI_FINALIZE(ierr)
stop
end

Page 75

 各プロセスが担当する
区間の計算を実施．
 計算結果をランク0に集めます．

 各プロセスから転送する配列のサイズ バリア処理 時間計測区間の終了．

８．演習問題6 (practice_6) つづき 手順③：コンパイルを実行します．
% mpinfort practice6.f 手順④：ジョブを投入します．
% qsub run.sh 手順⑤：実行結果を確認します．結果はpractice_p6.oXXXX(XXXXにはジョブIDが入ります)として格納されます．
% cat practice_p6.oXXXX

Page 76

付

録

付録１．主な手続き
付録２．参考文献，Webサイト

Page 77

付録１. 主な手続き
付録１.１
付録１.２
付録１.３
付録１.４
付録１.５

プロセス管理一対一通信
集団通信
その他の手続きプログラミング作法※本テキストでは，コミュニケータ（comm）は，MPI_COMM_WORLDとする．
Page 78

付録１.１ プロセス管理

Page 79

付録１.１.１

プロセス管理とは

MPI環境の初期化・終了処理や環境の問い合わせを行う

Page 80

付録１.１.２

プログラム例（FORTRAN）

include 'mpif.h‘
integer :: myrank,nprocs,ist,ied,ierr,isum1
parameter(numdat=100)
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD, nprocs, ierr)call MPI_COMM_RANK(MPI_COMM_WORLD, myrank, ierr)
ist=((numdat-1)/nprocs+1)*myrank+1
ied=((numdat-1)/nprocs+1)*(myrank+1)
isum1=0do i=ist,ied
isum1=isum1+i
enddo
call MPI_REDUCE(isum1,isum,1,MPI_INTEGER,MPI_SUM,&
0,MPI_COMM_WORLD,ierr)
if (myrank.eq.0) write(6,*)'sum=',isum
call MPI_FINALIZE(ierr)stop
endPage 81

etc6.f付録１.１.２

プログラム例（C）#include <stdio.h>#include "mpi.h"
int main( int argc, char* argv[] ){int numdat=100;
int myrank, nprocs;
int i,ist,ied,isum1,isum;
MPI_Init( &argc, &argv );MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
ist=((numdat-1)/nprocs+1)*myrank+1;
ied=((numdat-1)/nprocs+1)*(myrank+1);
isum1=0;
for(i=ist;i<ied+1;i++) isum1 += i;
MPI_Reduce(&isum1,&isum,1,MPI_INT,MPI_SUM,
0,MPI_COMM_WORLD);
if(myrank==0) printf("isum=%d¥n",isum);
MPI_Finalize();}
Page 82

etc7.c付録１.１.３

インクルードファイル

書式

include 'mpif.h'…FORTRAN#include "mpi.h"

…C

メモ






MPI手続きを使うサブルーチン・関数では，必ずインクルードしなければならない
MPIで使用する MPI_xxx といった定数を定義している
ユーザは，このファイルの中身まで知る必要はない
mpif.h：INTEGER MPI_LOR, MPI_BOR, MPI_LXOR, MPI_BXOR,INTEGER MPI_MAXLOC, MPI_REPLACE
PARAMETER (MPI_MAX = 100)
PARAMETER (MPI_MIN = 101)
PARAMETER (MPI_SUM = 102)：

Page 83付録１.１.４

MPI_INIT MPI環境の初期化

機能概要





MPI環境の初期化処理を行う
引数は返却コード ierr のみ（ FORTRANの場合）

書式

integer ierr
CALL MPI_INIT（ierr)
int MPI_Init (int *argc, char ***argv)
メモ





他のMPIルーチンより前に１度だけ呼び出されなければならない
返却コードは，コールしたMPIルーチンが正常に終了すれば，
MPI_SUCCESSを返す（他のMPIルーチンでも同じ）
当該手続きを呼び出す前に設定した変数・配列は，他のプロ
セスには引き継がれない（引き継ぐには通信が必要）
P9に戻る

Page 84

付録１.１.５

MPI_FINALIZE MPI環境の終了

機能概要




MPI環境の終了処理を行う
引数は返却コード ierr のみ（FORTRANの場合）

書式

integer ierr
CALL MPI_FINALIZE(ierr)
int MPI_Finalize (void)
メモ





プログラムが終了する前に，必ず１度実行する必要がある― 異常終了処理には，MPI_ABORTを用いる
この手続きが呼び出された後は，いかなるMPIルーチンも呼び出してはならない
P9に戻る

Page 85

付録１.１.６

MPI_ABORT

MPI環境の中断

機能概要



MPI環境の異常終了処理を行う

書式

integer comm, errcode, ierr
CALL MPI_ABORT(comm, errcode, ierr)
int MPI_Abort (MPI_Comm comm, int errcode)
引数

引数

値

入出力

comm

handle IN

コミュニケータ

errcode整数

エラーコード

IN

メモ





Page 86

すべてのプロセスを即時に異常終了しようとする
引数にコミュニケータを必要とするがMPI_COMM_WORLDを想定

付録１.１.７ MPI_COMM_SIZE MPIプロセス数の取得
機能概要


書式

指定したコミュニケータにおける全プロセス数を取得する
integer comm, nprocs, ierr
CALL MPI_COMM_SIZE（comm, nprocs, ierr)
int MPI_Comm_size (MPI_Comm comm, int *nprocs)

引数

引数

値

入出力

comm

handleIN

コミュニケータ

nprocs

整数

OUT

コミュニケータ内の総プロセス数

メモ

commがMPI_COMM_WORLDの場合，利用可能なプロセスの総数を返す
P10に戻る

Page 87

付録１.１.８

MPI_COMM_RANK ランク番号の取得

機能概要


書式

指定したコミュニケータにおける自プロセスのランク番号を取得する
integer comm, myrank, ierr
CALL MPI_COMM_RANK(comm, myrank, ierr)
int MPI_Comm_rank(MPI_Comm comm, int *myrank)

引数

引数

値

入出力

comm

handleIN

コミュニケータ

myrank

整数

OUT

コミュニケータ中のランク番号メモPage 88

自プロセスと他プロセスの区別，認識に用いる
0からnproc-1までの範囲で 呼び出したプロセスのランクを返P10に戻るす（nprocsはMPI_COMM_SIZEの返却値）

付録１.１.９ ランク番号と総プロセス数を使った処理の分割▍ 1から100までをnprocで分割
myrank=0
nprocs=4

myrank=1
nprocs=4

myrank=2
nprocs=4

myrank=3
nprocs=4

ist = ((100-1)/nprocs+1)*myrank+1
ied = ((100-1)/nprocs+1)*(myrank+1)

ist = ((100-1)/4+1)*0+1=1
ist = ((100-1)/4+1)*1+1
ied = ((100-1)/4+1)*(0+1)= 26
ist = ((100-1)/4+1)*2+1= 25
ied = ((100-1)/4+1)*(1+1)= 51
ist = ((100-1)/4+1)*3+1= 50
ied = ((100-1)/4+1)*(2+1)= 76= 75
ied = ((100-1)/4+1)*(3+1)= 100

Page 89

付録１.２ 一対一通信

Page 90

付録１.２.１

一対一通信とは 一組の送信プロセスと受信プロセスが行うメッセー
ジ交換 メッセージの交換は，データを送受信することで行われる 一対一通信は，送信処理と受信処理に分かれている ブロッキング型通信と非ブロッキング型通信がある

Page 91

付録１.２.２

プログラム例▍ 1から100までをnprocで分割
逐次版(etc8.f)
integer a(100),isumopen(10,file='fort.10')
read(10,*) a
isum=0do i=1,100
isum=isum+a(i)enddo
write(6,*)‘SUM=',isum
stop
end

Page 92

付録１.２.２

処理イメージ

並列処理イメージ

filedata

ランク０
ランク１

dataarea

ランク２
ランク３

Page 93付録１.２.３

Page 94

プログラム例（MPI版）

include 'mpif.h'parameter(numdat=100)
integer status(MPI_STATUS_SIZE),senddata(numdat),recvdata(numdat)
integer source,dest,tag
integer myrank,nprocs,icount,ierr,isum
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)icount=(numdat-1)/nprocs+1
if(myrank.eq.0)thenopen(10,file='fort.10')
read(10,*) senddata
do i=1,nprocs-1
dest=itag=myrankcall MPI_SEND(senddata(icount*i+1),icount,MPI_INTEGER,&
dest,tag,MPI_COMM_WORLD,ierr)enddo
recvdata=senddata
elsesource=0
tag=source
call MPI_RECV(recvdata(icount*myrank+1),icount,MPI_INTEGER,&
source,tag,MPI_COMM_WORLD,status,ierr)
endif
isum=0do i=1,icountisum=isum+recvdata(icount*myrank+i)
enddo
call MPI_FINALIZE(ierr)write(6,*) myrank,':SUM= ',isum
stop ; end

etc9.f付録１.２.４

MPI_SEND ブロッキング型送信

機能概要



送信バッファ(data)内のデータ型がdatatypeで連続したcount個のタグ(tag)付き要素をコミュニケータcomm内のランク
destなるプロセスに送信する

処理イメージ

送信側プロセス
送信バッファ(data)
count&
datatype

tag
６５
３８
８１

Page 95

送信先プロセス
destへ付録１.２.４

MPI_SEND ブロッキング型送信

書式

任意の型 data(*)
integer count,datatype,dest,tag,comm,ierr
CALL MPI_SEND（data,count,datatype,dest,tag,comm,ierr)
int MPI_Send (void* data,int count,MPI_Datatype datatype,int dest,int tag,MPI_Comm comm)
引数

引数

値

入出力

data

任意

IN

送信データの開始アドレス

count

整数

IN

送信データの要素数(0以上の整数)

datatype handle IN

Page 96

送信データのタイプ

dest

整数

IN

通信相手のランク

tag

整数

IN

メッセージタグ

commhandle IN

コミュニケータ付録１.２.４ MPI_SENDブロッキング型送信（続き）
メモ









Page 97

メッセージの大きさはバイト数ではなく，要素の個数(count)で表す
datatypeは次ページ以降に一覧を示す
タグはメッセージを区別するために使用する
本ルーチン呼び出し後，転送処理が完了するまで処理を待ち合せる
MPI_SENDで送信したデータは，MPI_IRECV，MPI_RECVのどちらで受信してもよい付録１.２.５ MPIで定義された変数の型（FORTRAN）
MPIのデータタイプMPI_INTEGERMPI_INTEGER2MPI_INTEGER4MPI_REALMPI_REAL4
MPI_REAL8MPI_DOUBLE_PRECISIONMPI_REAL16
MPI_QUADRUPLE_PRECISIONMPI_COMPLEXMPI_COMPLEX8MPI_COMPLEX16
MPI_DOUBLE_COMPLEXMPI_COMPLEX32
MPI_LOGICALMPI_LOGICAL1MPI_LOGICAL4MPI_CHARACTER
Page 98

FORTRAN言語の対応する型
INTEGERINTEGER*2
INTEGER*4
REAL
REAL*4
REAL*8DOUBLE PRECISIONREAL*16QUADRUPLE PRECISIONCOMPLEXCOMPLEX*8
COMPLEX*16
DOUBLE COMPLEXCOMPLEX*32
LOGICALLOGICAL*1
LOGICAL*4
CHARACTERなど

付録１.２.６

MPIで定義された変数の型（C）

MPI
データタイプ

C言語
対応する型

MPI_CHARMPI_SHORTMPI_INTMPI_LONG
MPI_LONG_LONG
MPI_LONG_LONG_INT
MPI_UNSIGNED_CHAR
MPI_UNSIGNED_SHORTMPI_UNSIGNED_INT
MPI_UNSIGNED_LONG
MPI_FLOATMPI_DOUBLEMPI_LONG_DOUBLE

char
short
int
long
long long
long longunsigned char
unsigned shortunsigned int
unsigned long
float
doublelong doubleなど

P32に戻る

Page 99

付録１.２.７

MPI_RECV ブロッキング型受信

機能概要



コミュニケータcomm内のランクsourceなるプロセスから送信されたデータ型がdatatypeで連続したcount個のタグ(tag)付き要素を受信バッファ(data)に同期受信する処理イメージtag

受信側プロセス
受信バッファ(data)

６５
送信元プロセス
sourceから

Page 100

３８
８１

count
&
datatype

付録１.２.７ MPI_RECV ブロッキング型受信（続き）
任意の型 data(*)
integer count, datatype, source, tag, comm,status(MPI_STATUS_SIZE), ierr
CALL MPI_RECV(data,count,datatype,source,tag,comm,status,ierr)

書式

int MPI_Recv (void* data, int count, MPI_Datatype
datatype, int source, int tag, MPI_Comm comm,
MPI_Status *status)

引数

引数

値

入出力

data

任意

OUT

受信データの開始アドレス

count

整数

IN

受信データの要素の数（0以上の値）

datatype

handleIN

受信データのタイプ

source

整数IN

通信相手のランク

tag

整数

IN

メッセージタグ

commhandleIN

コミュニケータstatusstatus

OUT

メッセージ情報

Page 101

付録１.２.７ MPI_RECV ブロッキング型受信（続き）

メモ

転送処理が完了するまで処理を待ち合せる
引数statusは通信の完了状況が格納される






FORTRANでは大きさがMPI_STATUS_SIZEの整数配列
CではMPI_Statusという型の構造体で，送信元やタグ，エラーコードな
どが格納される

P32に戻る

Page 102

付録１.２.８

ブロッキング型通信の動作▍ MPI_SEND,MPI_RECV

送信側

受信側

MPI_SEND：①送信指示

待ち①受信指示：MPI_RECVMPI通信ライブラリ

MPI通信ライブ
ラリ

②送信

③送信完了

③受信完了

処理の流れ

Page 103待ち付録１.２.９

MPI_ISEND 非ブロッキング型送信

機能概要



送信バッファ(data)内のデータ型がdatatypeで連続したcount個のタグ(tag)付き要素をコミュニケータcomm内のランク
destなるプロセスに送信する

処理イメージ

送信側プロセス
送信バッファ(data)
count&
datatype

Page 104

tag
６５
３８
８１

受信側プロセス
destへ付録１.２.９ MPI_ISEND 非ブロッキング型送信（続き）
任意の型 data(*)
integer count,datatype,dest,tag,comm,request,ierr
CALL MPI_ISEND(data,count,datatype,dest,tag,comm,request,ierr)

書式

int MPI_Isend (void* data, int count,
MPI_Datatype datatype, int dest, int tag,
MPI_Comm comm, MPI_Request *request)

引数

引数

値

入出力

data

任意

IN

送信データの開始アドレス

count

整数

IN

送信データの要素の数（0以上の値）

datatype

handleIN

送信データのタイプ

dest

整数

IN

通信相手のランク

tag

整数

IN

メッセージタグ

commhandleIN

コミュニケータ

requesthandle

OUT

通信識別子

Page 105

付録１.２.９ MPI_ISEND 非ブロッキング型送信（続き）

メモ










メッセージの大きさはバイト数ではなく，要素の個数(count)で表す
datatypeはMPI_SENDの項を参照
タグはメッセージを区別するために使用する
requestには要求した通信の識別子が戻され，MPI_WAIT等で通信の完了を確認する際に使用する
本ルーチンコール後，受信処理の完了を待たずにプログラムの処理を続行する
MPI_WAITまたはMPI_WAITALLで処理の完了を確認するまで
は，dataの内容を更新してはならない
MPI_ISENDで送信したデータは，MPI_IRECV，MPI_RECVのどちらで受信してもよい
通信の完了もMPI_WAIT，MPI_WAITALLのどちらを使用してもよい
P52に戻る

Page 106

付録１.２.10

MPI_IRECV 非ブロッキング型受信

機能概要



コミュニケータcomm内のランクsourceなるプロセスから送信されたデータ型がdatatypeで連続したcount個のタグ(tag)付き要素を受信バッファ(data)に受信する処理イメージ

受信側プロセス
tag

受信バッファ(data)
６５

送信側プロセス
sourceから

Page 107

３８
８１

count
&
datatype

付録１.２.10 MPI_IRECV 非ブロッキング型受信（続き）
書式

引数

任意の型 data(*)
integer count,datatype,source,tag,comm,request,ierr
CALL MPI_IRECV(data,count,datatype,source,tag,comm,request,ierr)
int MPI_Irecv (void* data, int count, MPI_Datatype
datatype, int source, int tag, MPI_Comm comm,
MPI_Request *request)

引数

値

入出力

data

任意

OUT

受信データの開始アドレス

count

整数

IN

受信データの要素の数(0以上の値)

datatype handle IN

Page 108

受信データのタイプ

source

整数IN

通信相手のランク

tag

整数

IN

メッセージタグ

commhandle IN

コミュニケータ

requeststatus

メッセージ情報OUT

付録１.２.10 MPI_IRECV 非ブロッキング型受信（続き）
メモ










メッセージの大きさは要素の個数(count)で表す
datatypeはMPI_SENDの項を参照
タグは送信側で付けられた値もしくは，MPI_ANY_TAGを指定する
requestは要求した通信の識別子が戻され，MPI_WAIT等で通信の完了を確認する際に使用する
本ルーチンコール後，処理の完了を待たずにプログラムの処理を続行する
MPI_WAITまたはMPI_WAITALLで処理の完了を確認するまでは，dataの内容を使用してはならない
MPI_ISEND，MPI_SENDのどちらで送信したデータも
MPI_IRECVで受信してよい
通信の完了もMPI_WAIT，MPI_WAITALLのどちらを使用してもよい
P52に戻る

Page 109

付録１.２.11

非ブロッキング型通信の動作▍ MPI_ISEND,MPI_IRECVの動作
送信側

受信側①受信指示：MPI_IRECVMPI_ISEND：①送信指示
MPI_WAITMPI通信ライブラリ

MPI通信ライブ
ラリ

②通信

待ち

待ち

③送信完了

③受信完了

処理の流れ

Page 110

MPI_WAIT付録１.２.12

MPI_WAIT 通信完了の待ち合わせ

機能概要


書式

非同期通信処理が完了するまで待ち合わせる
integer request, status(MPI_STATUS_SIZE), ierr
CALL MPI_WAIT(request, status, ierr)
int MPI_Wait(MPI_Request *request, MPI_Status *status)

引数

引数

値

入出力

requesthandle

INOUT

通信識別子

statusstatusout

メッセージ情報メモrequestには，MPI_ISEND，MPI_IRECVをコールして返されたメッセージ情報requestを指定する
statusには，FORTRANではMPI_STATUS_SIZEの整数配列，CではMPI_Status型の構造体を指定する
P52に戻る

Page 111

付録１.２.13

MPI_WAITALL 通信完了の待合わせ

機能概要• 1つ以上の非同期通信全ての完了を待ち合わせる

書式

integer count, array_of_requests(count),array_of_status(MPI_STATUS_SIZE,*), ierrcall MPI_WAITALL(count,array_of_requests,
array_of_status,ierr)
int MPI_Waitall(int count,
MPI_Request *array_of_requests,
MPI_Status *array_of_status)

引数

引数

Page 112

値

入出力

count

整数

array_of_requests

handle INOUT

通信識別子の配列
大きさは(count)array_of_status

status

メッセージ情報の配列
大きさは(count)

IN

OUT

待ち合わせる通信の数付録１.２.13 MPI_WAITALL 通信完了の待合わせ（続き）

メモ





Page 113

array_of_statusは，Fortranでは整数配列で大きさは(count,MPI_STATUS_SIZE)
CではMPI_Statusの構造体の配列で，大きさは(count)array_of_statusには，array_of_requestsに指定された
requestと同じ順番で，そのrequestに対応する通信の完了状
態が格納される

付録１.２.14

一対一通信まとめ

送信

受信

同期通信

MPI_SENDMPI_RECV

非同期通信

MPI_ISEND

MPI_IRECV

待ち合せ
MPI_WAIT(ALL)MPI_SEND,MPI_ISENDのどちらで送信した場合でも，
MPI_RECV,MPI_IRECVのどちらで受信してもよい(“I”は immediate の頭文字)



MPI_ISEND，MPI_IRECVは，MPI_WAITで個別に待ち合わ
せてもMPI_WAITALLでまとめて待ち合わせても良い

Page 114

付録１.３ 集団通信

Page 115

付録１.３.１

集団通信とは コミュニケータ内の全プロセスで行う同期的通信• 総和計算などのリダクション演算• 入力データの配布などに用いられるブロードキャ
スト• FFTで良く用いられる転置• その他ギャザ／スキャッタなど

Page 116

付録１.３.２

プログラム例
etc10.finclude 'mpif.h'parameter(numdat=100)
integer :: myrank,nprocs,ist,ied,ierr,isum1,isum
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
ist=((numdat-1)/nprocs+1)*myrank+1
ied=((numdat-1)/nprocs+1)*(myrank+1)
isum1=0do i=ist,ied
isum1=isum1+i
enddo
call MPI_REDUCE(isum1,isum,1,MPI_INTEGER,MPI_SUM,&
0,MPI_COMM_WORLD,ierr)if(myrank.eq.0)write(6,*)'isum=',isum
call MPI_FINALIZE(ierr)
1 2 … 24 25
…
…
stop
end
…

isum1 ３２５

isum ５０５０
Page 117

26

27

…

49

プロセス毎の小計しかわからない

各プロセスの小計を集
計する
51

52

50

…

…

74

75

…

…

76

77

…

99

100

１５７５
９５０

２２００

各ランクのisum1を通信しながら総和計算しランク0のisumに格納

付録１.３.３
機能概要





MPI_REDUCE

コミュニケータcomm内の全プロセスが，送信バッファのデータ(senddata)を通信しながら，opで指定された演算を行い，結果を宛先(root)プロセスの受信バッファ(recvdata)に格納する送信データが配列の場合は，要素毎に演算を行う
root

処理イメージ
ランク０
senddata
count &
datatype

op

ランク１

ランク２

ランク３

３８

１０

６０

９０

９６

１

４１

８６

５

２５

３

１６

最大値探索

recvdata
count &
datatype

call MPI_REDUCE(senddata,recvdata,3,９０&

MPI_INTEGER,MPI_MAX,

９６&0,MPI_COMM_WORLD,ierr)

２５Page 118

リダクション演算付録１.３.３ MPI_REDUCE リダクション演算（続き）
書式

任意の型 senddata(*), recvdata(*)
integer count, datatype, op, root, comm, ierrcall MPI_REDUCE(senddata, recvdata, count, datatype, op,root, comm, ierr)
int MPI_Reduce(void* senddata, void* recvdata, int count,
MPI_Datatype datatype, MPI_Op op, int root,
MPI_Comm comm)

引数

Page 119

引数

値

入出力

senddata

任意IN

送信データのアドレス

recvdata

任意

OUT

受信データのアドレス(rootプロセスだけ意味を持つ)

count

整数

IN

送信データの要素の数

datatype

handle IN

送信データのタイプ

ophandle IN

リダクション演算の機能コード

root

整数

rootプロセスのランク

commhandle ININ

コミュニケータ付録１.３.３ MPI_REDUCE リダクション演算（続き）▍ MPI_REDUCEで使える演算
機能名
MPI_MAXMPI_MIN
MPI_SUM
MPI_PROD
MPI_MAXLOCMPI_MINLOC
MPI_BANDMPI_BORMPI_BXORMPI_LANDMPI_LORMPI_LXOR

Page 120

機能
最大値
最小値
総和
累積
最大値と対応情報取得
最小値と対応情報取得
ビット積
ビット和
排他的ビット和
論理積
論理和
排他的論理和付録１.３.４

総和計算の丸め誤差

総和計算において，逐次処理と並列処理とで結果が異なる場合がある↓
並列処理に限らず，部分和をとってから総和を算出する等，加算順序の変更により結果が異なっている可能性がある
例： (有効桁数を小数点以下4桁として)配列aに右の数値が入っていたとする 1E+5

=1.0000E+5
同様に a(3),a(4),a(5)まで足し込んだdsumは 1.0000E+5
dsum=dsum+a(6)=1.0000E+5 + 1.0000E+5=2.0000E+548dsum1=a(1)+a(2)=1E5+0.00007E5=1.0000E+5
dsum1+a(3)=1E5+0.00004E5=1.0000E+5
dsum2=a(4)+a(5)=8+6=14=0.0001E5
dsum2+a(6)=0.0001E5+1E5=1.0001E+5
dsum=dsum1+dsum2=1.0000E+5 + 1.0001E+5=2.0001E+5
加算順序の違いで異なる結果になった

Page 121

6 1E+5２並列

逐次処理
dsum=a(1)+a(2)=1E5+0.00007E5
有効桁数以下切捨てで7

付録１.３.４

注意事項▌ 通信に参加する全プロセスが，同じ集団通信手続きをコールしなければならない▌ 送信バッファと受信バッファの実際に使用する部分は，メモリ上で重なってはならない（MPI-2では，MPI_IN_PLACEを用いることで可能になります)▌基本的に集団通信処理の直前や直後での同期処理は不要

P38に戻る
Page 122

付録１.３.５
機能概要


MPI_ALLREDUCE

リダクション演算

コミュニケータcomm内の全プロセスが，送信バッファのデータ(senddata)を通信しながら，opで指定された演算を行い，結果を全プロセスの受信バッファ(recvdata)に格納する処理イメージ
ランク０

ランク１

ランク２

ランク３

senddata
count &
datatype

３８

１０

６０

９０

９６

１

４１

８６

２５

３

１６

９０

９０

９０

９０

９６

９６

９６

９６

２５

２５

２５

２５

５

最大値探索

recvdata
count &
datatype

opcall MPI_ALLREDUCE(senddata,recvdata,3,MPI_INTEGER,MPI_MAX,&

Page 123

MPI_COMM_WORLD,ierr)付録１.３.５ MPI_ALLREDUCE リダクション演算（続き）
書式

任意の型 senddata(*), recvdata(*)
integer count, datatype, op, comm, ierrcall MPI_ALLREDUCE(senddata, recvdata, count, datatype, op,
comm, ierr)
int MPI_Allreduce(void* senddata, void* recvdata, int count,
MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
引数

メモ

引数

値

入出力

senddata

任意IN

送信データのアドレス

recvdata

任意

OUT

受信データのアドレス

count

整数

IN

送信データの要素の数

datatype

handleIN

送信データのタイプ

ophandleIN

リダクション演算の機能コード

commhandleIN

コミュニケータ MPI_REDUCEの計算結果を全プロセスに送信するのと機能的に同じ
Page 124

付録１.３.６

MPI_BCAST ブロードキャスト

機能概要



１つの送信元プロセス(root)の送信バッファ(data)のデータをコミュニケータcomm内全てのプロセスの受信バッファ(data)に送信する

処理イメージroot
data
count &
datatype

Page 125

ランク０

ランク１

ランク２

ランク３

A

A

A

A

B

B

B

B

付録１.３.６

MPI_BCAST ブロードキャスト（続き）

任意の型 data(*)
integer count,datatype,root,comm,ierrcall MPI_BCAST(data,count,datatype,root,comm,ierr)

書式

int MPI_Bcast(void* data, int count, MPI_Datatype
datatype, int root, MPI_Comm comm)
引数

引数

値

入出力

data

任意

INOUT データの開始アドレス

count

整数

IN

datatype handle IN

メモ

root

整数

comm

handle IN

IN

データの要素の数

データのタイプ
ブロードキャスト送信プロセスのランク

コミュニケータ・ dataはrootプロセスでは送信データ，その他のプロセスでは受信データになる
P37に戻る

Page 126

付録１.３.７

プログラム例（総和計算）
etc11.finclude 'mpif.h'parameter(numdat=100)
integer isum_arry(10)integer myrank,nprocs,ist,ied,ierr,isum,isum1
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
ist=((numdat-1)/nprocs+1)*myrank+1
ied=((numdat-1)/nprocs+1)*(myrank+1)
isum1=0do i=ist,ied
isum1=isum1+i
enddo
call MPI_GATHER(isum1, 1, MPI_INTEGER, isum_arry, 1,&
MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)if(myrank.eq.0) thenisum=0
isum1 325
950
1575
do i=1,nprocs
isum=isum+isum_arry(i)
325
enddo
950
write(6,*)'isum=',isum1575 isum_arry
endifcall MPI_FINALIZE(ierr)
2200
stop
end

isum

Page 127

5050

2200

付録１.３.８
機能概要

MPI_GATHER データの集積

コミュニケータcomm内の全プロセスの送信バッファ(senddata)から， １つのプロセス(root)の受信バッファ(recvdata)へメッ
セージを送信するメッセージの長さは一定で，送信元プロセスのランクが小さい順に受信バッファに格納される処理イメージ
ランク０
senddata
sendcount&sendtype

root

ランク１

ランク２

ランク３

３８

１０

６０

９０

９６

１

４１

８６

５

２５

３

１６

recvdata

３８

１０

６０

９０

call MPI_GATHER(senddata,3,MPI_INTEGER,

recvcount&recvtype

９６

１

４１

８６

&

recvdata,3,MPI_INTEGER,

５

２５

３

１６

&

0,MPI_COMM_WORLD,ierr)(プロセス数)
Page 128

付録１.３.８

MPI_GATHER データの集積（続き）

書式

任意の型 senddata(*), recvdata(*)
integer sendcount, sendtype, recvcount, recvtype,root, comm, ierrcall MPI_GATHER(senddata, sendcount, sendtype,recvdata, recvcount, recvtype,root, comm, ierr)

int MPI_Gather(void* senddata, int sendcount,
MPI_Datatype sendtype, void* recvarea,
int recvcount, MPI_Datatype recvtype,
int root, MPI_Comm comm)Page 129

付録１.３.８

MPI_GATHER データの集積（続き）

引数

引数

値

入出力

senddata

任意IN

送信データの開始アドレス

sendcount 整数

IN

送信データの要素の数

sendtype

handle IN

送信データのタイプ

recvdata

任意OUT

受信領域の開始アドレス

recvcount

整数IN

個々のプロセスから受信する要素数 ☆

recvtype

handle IN

受信領域のデータタイプ

root

整数

rootプロセスのランク

commhandle IN

メモ

IN

☆
☆

コミュニケータ☆…rootプロセスだけ意味を持つ・ メッセージの長さは一定で，送信元プロセスのランクが小さい順に受信バッファに格納される
P40に戻る
P60に戻る
Page 130

付録１.３.９

MPI_GATHERV データの集積

機能概要




コミュニケータcomm内の全プロセスの送信バッファ(senddata)から，１つのプロセス(root)の受信バッファ(recvdata)へメッセージを送信する
送信元毎に受信データ長(recvcnt)と受信バッファ内の位置(displs)を変えることができる

処理イメージ

ランク０

senddata

root

ランク１

３８

１０
１

sendcount &sendtype

recvdata

displs(0) ＝ ０
displs(1) ＝ １
displs(2) ＝ ３

Page 131

３８
１０
１
６０
４１
３

ランク２

recvcount(0) ＝１
recvcount(0) ＝２
recvcount(0) ＝３

６０
４１
３

付録１.３.９

MPI_GATHERV データの集積（続き）

書式

任意の型 senddata(*),recvdata(*)
integer sendcount, sendtype, recvcount(*),
displs(*), recvtype,root, comm, ierrcall MPI_GATHERV(senddata, sendcount, sendtype,recvdata, recvcount, displs,
recvtype, root, comm, ierr)

int MPI_Gatherv(void* senddata, int sendcount,
MPI_Datatype sendtype, void* recvdata,
int *recvcount, int *displs,
MPI_Datatype recvtype, int root,
MPI_Comm comm)Page 132

付録１.３.９

MPI_GATHERV データの集積（続き）

引数

値

入出力

senddata

任意IN

送信データの開始アドレス

sendcount

整数

IN

送信データの要素のsendtype

handleIN

送信データのタイプ

recvdata

任意OUT

受信領域の開始アドレス☆

recvcount

整数IN

個々のプロセスから受信する
要素数の配列☆

displs

整数IN

受信データを置き始めるrecvdataからの相対位置の配列☆

recvtype

handleIN

受信領域のデータタイプ

root

整数

IN

rootプロセスのランク

commhandleIN

コミュニケータ☆

☆ …rootプロセスだけが意味を持つ
P40に戻る
Page 133

付録１.３.10 MPI_ALLGATHER 全プロセスでデータ集積
機能概要





コミュニケータ(comm)内の全プロセスの送信バッファ(senddata)から，全プロセスの受信バッファ(recvdata)へ互いにメッセージを送信する
メッセージの長さは一定で，送信元プロセスのランクが小さい順に受信バッファに格納される処理イメージ

ラ
ン
ク
０

３８
９６
５

ラ
ン
ク
１

３８
９６
５

１０
１
２５

６０
４１
３

９０
８６
１６

３８
９６
５

ラ
ン
ク
２

１０
１
２５

１０
１
２５

６０
４１
３

９０
８６
１６

call MPI_ALLGATHER(senddata,3,MPI_INTEGER,&
recvdata,3,MPI_INTEGER,&
0,MPI_COMM_WORLD,ierr)

Page 134

３８
９６
５

ラ
ン
ク
３

６０
４１
３

１０
１
２５

６０
４１
３

９０
８６
１６

３８
９６
５

９０
８６
１６

１０
１
２５

６０
４１
３

９０
８６
１６

付録１.３.10 MPI_ALLGATHER 全プロセスでデータ集積(続き)書式

任意の型 senddata(*), recvdata(*)
integer sendcount, sendtype, recvcount, recvtype,comm, ierrcall MPI_ALLGATHER(senddata, sendcount, sendtype,recvdata, recvcount, recvtype,comm, ierr)
int MPI_Allgather(void* senddata, int sendcount,
MPI_Datatype sendtype, void* recvdata,
int recvcount, MPI_Datatype recvtype,
MPI_Comm comm)Page 135

付録１.３.10 MPI_ALLGATHER 全プロセスでデータ集積(続き)
引数

引数

値

入出力

senddata

任意IN

送信領域の開始アドレス

sendcount

整数

IN

送信データの要素の数

sendtype

handleIN

送信データのタイプ

recvdata

任意OUT

受信領域の開始アドレス

recvcount

整数IN

個々のプロセスから受信する要素の数

recvtype

handleIN

受信データのタイプ

commhandleIN

コミュニケータ

メモ・ MPI_GATHERの結果を全プロセスに送信するのと機能的に同じ
Page 136

付録１.３.11 MPI_ALLGATHERV 全プロセスでデータ集積
機能概要




コミュニケータcomm内の全プロセスの送信バッファ(senddata)から，全プロセスの受信バッファ(recvdata)へメッ
セージを送信する
送信元毎に受信データ長(recvcount)と受信バッファ内の位置(displs)を変えることができる

処理イメージ

ランク０

root

ランク１

ランク２

senddata

３８
sendcount &sendtype

１０
１

６０
４１
３

３８
１０
１
６０
４１
３

３８
１０
１
６０
４１
３

recvdata

displs(0) ＝ ０
displs(1) ＝ １
displs(2) ＝ ３

Page 137

３８
１０
１
６０
４１
３

recvcount(0) ＝
１

recvcount(0) ＝２
recvcount(0) ＝３

付録１.３.11 MPI_ALLGATHERV 全プロセスでデータ集積
(続き)

書式

任意の型 senddata(*), recvdata(*)
integer sendcount, sendtype, recvcount(*), displs(*),
recvtype, comm, ierrcall MPI_ALLGATHERV(senddata, sendcount, sendtype,recvdata, recvcount, displs,
recvtype, comm, ierr)
int MPI_Allgatherv(void* senddata, int sendcount,
MPI_Datatype sendtype, void* recvdata,
int *recvcount, int *displs,
MPI_Datatype recvtype, MPI_Comm comm)Page 138

付録１.３.11 MPI_ALLGATHERV 全プロセスでデータ集積
(続き)

引数

引数

入出力

任意

IN

送信領域の開始アドレス

sendcount 整数

IN

送信データの要素の数

sendtype

handleIN

送信データのタイプ

recvdata

任意OUT

受信領域の開始アドレス

recvcount

整数

OUT

受信データの要素の数

displs

整数IN

受信データを置くrecvdataからの相対位置(プロセス毎)

recvtype

handleIN

受信データのタイプ

commhandleIN

コミュニケータ

senddata

Page 139値

付録１.３.12 プログラム例(代表プロセスによるファイル入力)
include 'mpif.h'
integer filedata(100),dataarea(100)integer :: myrank,nprocs,ist,ied,ierr,isum1,isum
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)icount=(100-1)/nprocs+1
if(myrank==0)thenopen(10,file='fort.10')
read(10,*)filedata
end ifcall MPI_SCATTER(filedata, icount, MPI_INTEGER,& dataarea(icount*myrank+1), icount, MPI_INTEGER,& 0, MPI_COMM_WORLD,ierr)isum1=0
ist=icount*myrank+1
ied=icount*(myrank+1)
do i=ist,ied
isum1=isum1+dataarea(i)enddocall MPI_REDUCE(isum1, isum, 1,& MPI_INTEGER, MPI_SUM,& 0, MPI_COMM_WORLD, ierr)if(myrank==0)& write(6,*)'sum=',isum
call MPI_FINALIZE(ierr)dataarea
stop
endPage 140etc12.f

filedata

付録１.３.13

MPI_SCATTER データの分配

機能概要




一つの送信元プロセス(root)の送信バッファ(senddata)から，コミュニケータcomm内の全てのプロセスの受信バッファ(recvdata)にデータを送信する
各プロセスへのメッセージ長は一定である

処理イメージ
root
senddata

sendcount &sendtype

recvdata
recvcount &recvtype

Page 141

ランク０

３８
９６
５

１０
１
２５

３８
９６
５

ランク１

６０
４１
３

ランク２

ランク３

９０
８６
１６

１０
１
２５

６０
４１
３

９０
８６
１６

付録１.３.13

MPI_SCATTER データの分配（続き）

書式

任意の型 senddata(*), recvdata(*),
integer sendcount, sendtype, recvcount, recvtype,root, comm, ierrcall MPI_SCATTER (senddata, sendcount, sendtype,recvdata, recvcount, recvtype,root, comm, ierr)

int MPI_Scatter(void* senddata, int sendcount,
MPI_Datatype sendtype, void* recvdata,
int recvcount, MPI_Datatype recvtype,
int root, MPI_Comm comm)Page 142

付録１.３.13

MPI_SCATTER データの分配（続き）

引数

引数

値

入出力

senddata

任意IN

送信領域のアドレス☆

sendcount 整数

IN

各プロセスへ送信する要素数☆

sendtype

handleIN

送信領域の要素のデータタイプ ☆

recvdata

任意

OUT

受信データのアドレス

recvcount 整数

IN

受信データの要素の数

recvtype

handleIN

受信データのタイプ

root

整数

IN

rootプロセスのランク

commhandleIN

コミュニケータ☆… rootプロセスだけ意味を持つ
P39に戻る

Page 143

P57に戻る付録１.３.14
機能概要




MPI_SCATTERV データの分配

一つの送信元プロセス(root)の送信バッファ(senddata)から，コミュニケータcomm内の全てのプロセスの受信バッファ(recvdata)にデータを送信する
送信先毎に送信データ長(sendcount)とバッファ内の位置(displs)を変えることができる

処理イメージ
senddata

ランク０

displs(0) ＝ ０
displs(1) ＝ １

３８
１０
１
６０
４１
３

displs(2) ＝ ３

recvdata
recvcount &recvtype
Page 144

３８

root

ランク１

ランク２

sendcount(0) ＝１
sendcount(1) ＝２
sendcount(2) ＝３

１０
１

６０
４１
３

付録１.３.14

MPI_SCATTERV データの分配（続き）

書式

任意の型 senddata(*), recvdata(*)
integer sendcount(*), displs(*), sendtype, recvcount,recvtype, root, comm, ierrcall MPI_SCATTERV(senddata, sendcount, displs, sendtype,recvdata, recvcount, recvtype, root,
comm, ierr)

int MPI_Scatterv(void* senddata, int *sendcount,int *displs, MPI_Datatype sendtype,void* recvdata, int recvcount,
MPI_Datatype recvtype, int root,
MPI_Comm comm)Page 145

付録１.３.14

MPI_SCATTERV データの分配（続き）

引数

引数

値

入出力

senddata

任意IN

送信領域のアドレス☆

sendcount

整数IN

各プロセスへ送信する要素数☆

displs整数IN

プロセス毎の送信データの始まる
senddataからの相対位置☆
☆

sendtype

handleIN

送信データのタイプ

recvdata

任意

OUT

受信データのアドレス

recvcount

整数

IN

受信データの要素の数

recvtype

handleIN

受信データのタイプ

root

整数

IN

rootプロセスのランク

commhandleIN

コミュニケータ☆… rootプロセスだけ意味を持つ
P39に戻る
Page 146

付録１.３.15

MPI_ALLTOALL

データ配置

機能概要




コミュニケータcomm内の全プロセスが，それぞれの送信
バッファ(senddata)から，他の全てのプロセスの受信バッ
ファ(recvdata)にデータを分配する
各プロセスへのメッセージ長は一定である

処理イメージ
senddata
sendcount &sendtype

ランク０

ランク１

ランク２

011

021

031

111

121

131

211

221

231

012

022

032

112

122

132

212

222

232

013

023

033

113

123

133

213

223

233

011

111

211

021

121

221

031

131

231

012

112

212

022

122

222

032

132

232

013

113

213

023

123

223

033

133

233

recvdata
recvcount &recvtype

Page 147

付録１.３.15

MPI_ALLTOALL

データ配置（続き）

書式

任意の型 senddata(*), recvdata(*)
integer sendcount, sendtype, recvcount, recvtype,comm, ierrcall MPI_ALLTOALL(senddata, sendcount, sendtype,recvdata, recvcount, recvtype,comm, ierr)
int MPI_Alltoall(void* senddata, int sendcount,
MPI_Datatype sendtype, void* recvdata,
int recvcount, MPI_Datatype recvtype,
MPI_Comm comm)Page 148

付録１.３.15

引数

引数senddata

MPI_ALLTOALL

値

データ配置（続き）

入出力

任意

IN

送信領域の開始アドレス

sendcount 整数
sendtype handle

IN

各プロセスへ送信する要素の数

IN

送信データのタイプ

recvdata

任意OUT

受信領域の開始アドレス

recvcount

整数IN

各プロセスから受信する要素の数

recvtype

handleIN

受信データのタイプ

commhandleIN

コミュニケータメモPage 149

全対全スキャッタ／ギャザ，または全交換とも呼ばれる

付録１.３.16
機能概要




MPI_ALLTOALLV

データ配置

コミュニケータcomm内の全プロセスが，それぞれの送信バッファ(senddata)から他の全てのプロセスの受信バッファ(recvdata)にデータを分配する
送信元毎にメッセージ長を変えることができる

処理イメージ
senddata

ランク０

sdispls(0) ＝ ０
sdispls(1) ＝ １

０１
０２
０３
０４
０５
０６

sendcount(0) ＝１
sendcount(1) ＝２

０１

recvcount(0)recvcount(1)recvcount(2)sdispls(2) ＝ ３

ランク１

sendcount(2) ＝３

ランク２
１１
１２
１３
１４
１５
１６

２１
２２
２３
２４
２５
２６

０２

０４

０３

０５

１２

０６

１３

１４

２２

１５

２３

２４

recvdata
rdispls(0)rdispls(1)rdispls(2)

１１
３１

２５
２６
Page 150

付録１.３.16

MPI_ALLTOALLV

データ配置

書式

任意の型 senddata(*), recvdata(*)
integer sendcount(*),sdispls(*), sendtype,recvcount(*),rdispls(*), recvtype,comm, ierrcall MPI_ALLTOALLV(senddata, sendcount, sdispls, sendtype,recvdata, recvcount, rdispls, recvtype,comm, ierr)
int MPI_Alltoallv(void* senddata, int *sendcount,int *sdispls, MPI_Datatype sendtype,void* recvdata, int *recvcount,int *rdispls, MPI_Datatype recvtype,
MPI_Comm comm)Page 151

付録１.３.16

MPI_ALLTOALLV

データ配置

引数

Page 152

引数

値

入出力

senddata

任意IN

送信領域の開始アドレス

sendcount

整数IN

送信する要素の数(プロセス毎)

sdispls

整数IN

送信データの始まるsenddataからの相対位置(プロセス毎)sendtype

handleIN

送信データのデータタイプ

recvdata

任意OUT

受信領域の開始アドレス

recvcount

整数IN

受信する要素の数(プロセス毎)

rdispls

整数IN

受信データを置き始めるrecvdataからの相対位置(プロセス毎)

recvtype

handleIN

受信バッファの要素のデータタイプ

commhandleIN

コミュニケータ付録１.４ その他の手続き

Page 153

付録１.４.１

計時（イメージ）

プロセス０

プロセス１

プロセス２

MPI_BARRIER MPI_BARRIERMPI_BARRIER
ｔ１

待

ち

MPI_BARRIER
待

ち待

MPI_BARRIERMPI_BARRIER

待ｔ２（測定時間）＝ｔ２－ｔ１

Page 154

ち

ち

測
定
範
囲

付録１.４.１

計時（プログラム例）
etc13.finclude 'mpif.h'parameter(numdat=100)
integer myrank,nprocs,ist,ied,ierr,isum1,isum
real*8 t1,t2,tt
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
ist=((numdat-1)/nprocs+1)*myrank+1
ied=((numdat-1)/nprocs+1)*(myrank+1)
call MPI_BARRIER(MPI_COMM_WORLD,ierr)
t1=MPI_WTIME()
isum=0do i=ist,ied
isum=isum+i
enddo
call MPI_REDUCE(isum,isum0,1,MPI_INTEGER,&MPI_SUM,0,MPI_COMM_WORLD,ierr)call MPI_BARRIER(MPI_COMM_WORLD,ierr)
t2=MPI_WTIME()
tt=t2-t1
if(myrank.eq.0)write(6,*)'sum=',isum0,',time=',tt
call MPI_FINALIZE(ierr)stop
endPage 155

付録１.４.２

MPI_WTIME 経過時間の測定

機能概要

•

過去のある時刻からの経過時間（秒数）を倍精度実数で返す

書式

DOUBLE PRECESION MPI_WTIME ( )double MPI_Wtime (void)
メモ



引数はない
この関数を実行したプロセスのみの時間を取得できる―Page 156

プログラム全体の経過時間を知るには同期を取る必要がある

得られる値は経過時間であり，システムによる中断があればその時間も含まれる

付録１.４.３

MPI_BARRIER

バリア同期

機能概要



コミュニケータ(comm)内の全てのプロセスで同期をとる

書式

integer comm,ierrcall MPI_BARRIER (comm, ierr)
int MPI_Barrier (MPI_Comm comm)
引数

引数

値

comm

handle入出力
IN

コミュニケータメモPage 157

MPI_BARRIERをコールすると，commに含まれる全てのプロ
セスが MPI_BARRIERをコールするまで待ち状態に入る

付録１.５

プログラミング作法

1. FORTRAN①②

ほとんどのMPI手続きはサブルーチンであり，引数の最後に整数型の返却コード（本書ではierr）を必要とする関数は引数に返却コードを持たない

2. C
①
②
③

接頭辞MPI_とそれに続く1文字は大文字，以降の文字は小文字
但し，定数はすべて大文字
ほとんどの関数は戻り値として返却コードを返すため，引数に返却コードは必
要ない

3. 共通
①

引数説明にある「handle」は，FORTRANでは整数型，Cでは書式説明に記載
した型を指定する② 引数説明にある「status」は，FORTRANではMPI_STATUS_SIZEの整数配列，CではMPI_Status型の構造体を指定する③ 接頭辞MPI_で始まる変数や関数は宣言しない方が良い④ 成功した場合の返却コードはMPI_SUCCESSとなる
Page 158

付録２．参考文献，Webサイト▌MPI並列プログラミング，Peter S. Pacheco著，秋葉博訳

出版社: 培風館 (2001/07)
ISBN-10: 456301544X
ISBN-13: 978-4563015442▌「並列プログラミング入門

ＭＰＩ版」

http://accc.riken.jp/wp-content/uploads/2015/06/secure_4467_parallel-programming_main.pdf▌「並列プログラミング虎の巻MPI版」
https://www.hpci-office.jp/invite2/documents2/mpi-all_20160801_20181206.pdf

Page 159

演習問題解答例

Page 160

演習問題1-2 （practice_1) 解答例
program example1
include 'mpif.h'
integer ierr,myrankcall MPI_INIT(ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)if(myrank.eq.0) write(6,*) "Hello World",myrankcall MPI_FINALIZE(ierr)
stop
end

Page 161

演習問題2 （practice_2) 解答例
program example2
include 'mpif.h'
integer ierr,myrank,nprocs,ist,ied
parameter(n=1000)
integer isumcall MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
ist=((n-1)/nprocs+1)* myrank+1
ied=((n-1)/nprocs+1)*(myrank+1)
isum=0do i=ist,ied
isum=isum+i
enddo
write(6,6000) myrank,isum6000 format("Total of Rank:",i2,i10)
call MPI_FINALIZE(ierr)stop
endPage 162

演習問題3 （practice_3) 解答例
program example3
include 'mpif.h'
integer ierr,myrank,nprocs,ist,ied,itag
integer status(MPI_STATUS_SIZE)
parameter(n=1000)
integer isum,isum2
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
ist=((n-1)/nprocs+1)*myrank+1
ied=((n-1)/nprocs+1)*(myrank+1)
isum=0do i=ist,ied
isum=isum+i
enddo
itag=1if(myrank.ne.0) thencall MPI_SEND(isum,1,MPI_INTEGER,0,&
itag,MPI_COMM_WORLD,ierr)else
call MPI_RECV(isum2,1,MPI_INTEGER,1,&
itag,MPI_COMM_WORLD,status,ierr)isum=isum+isum2
call MPI_RECV(isum2,1,MPI_INTEGER,2,&
itag,MPI_COMM_WORLD,status,ierr)isum=isum+isum2
call MPI_RECV(isum2,1,MPI_INTEGER,3,&
itag,MPI_COMM_WORLD,status,ierr)isum=isum+isum2
write(6,6000) isum6000 format("Total Sum = ",i10)
endifcall MPI_FINALIZE(ierr)
stop
end

Page 163

演習問題4 （practice_4) 解答例
program example4
include 'mpif.h'
integer ierr,myrank,nprocs,ist,ied
parameter(n=1000)
integer isum,isum2
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
ist=((n-1)/nprocs+1)*myrank+1
ied=((n-1)/nprocs+1)*(myrank+1)
isum=0do i=ist,ied
isum=isum+i
enddo
call MPI_REDUCE(isum,isum2,1,MPI_INTEGER,MPI_SUM,0,&
MPI_COMM_WORLD,ierr)if(myrank.eq.0) write(6,6000) isum2
6000 format("Total Sum = ",i10)
call MPI_FINALIZE(ierr)
stop
end
※

Page 164

MPI_REDUCEでは送信するデータと受信するデータの領域に重なりがあってはな
らない．isumとisum2に分けて使用．

演習問題5 （practice_5) 解答例
include 'mpif.h'
integer,parameter :: numdat=100
integer,allocatable :: senddata(:),recvdata(:)
call MPI_INIT(ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)
ist = ((numdat-1)/nprocs+1)*myrank+1
ied = ((numdat-1)/nprocs+1)*(myrank+1)
allocate(senddata(ist:ied))
if(myrank.eq.0) allocate(recvdata(numdat))icount=(numdat-1)/nprocs+1
do i=1,icountsenddata(icount*myrank+i)=icount*myrank+i
enddo

Page 165

演習問題5 （practice_5) 解答例（つづき）
call MPI_GATHER(senddata(icount*myrank+1),&
icount,MPI_INTEGER,recvdata,&
icount,MPI_INTEGER,0,MPI_COMM_WORLD,&
ierr)
if(myrank.eq.0) thenopen(60,file='fort.60')
write(60,'(10I8)') recvdata
deallocate(recvdata)
endif
deallocate(senddata)
call MPI_FINALIZE(ierr)
stop
end

Page 166

演習問題6(practice_6) 解答例

program example6
implicit real(8)(a-h,o-z)
include 'mpif.h'
integer ierr,myrank,nprocs,ist,ied
parameter ( n=12000 )real(8) a(n,n),b(n,n),c(n,n)
real(8) d(n,n)
real(8) t1,t2
call MPI_INIT(ierr)call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
ist=((n-1)/nprocs+1)*myrank+1
ied=((n-1)/nprocs+1)*(myrank+1)
n2=n/nprocs

Page 167

演習問題6(practice_6) 解答例（つづき）

do j = 1,ndo i = 1,na(i,j) = 0.0d0b(i,j) = n+1-max(i,j)
c(i,j) = n+1-max(i,j)
enddo
enddo
if(myrank.eq.0) thenwrite(6,50) ' Matrix Size = ',nendif
50 format(1x,a,i5)

Page 168

演習問題6(practice_6) 解答例（つづき）

call MPI_BARRIER(MPI_COMM_WORLD,ierr)
t1=MPI_WTIME()
do j=ist,ied
do k=1,ndo i=1,na(i,j)=a(i,j)+b(i,k)*c(k,j)
end doend doend docall MPI_GATHER(a(1,ist),n*n2,MPI_REAL8,d,n*n2&,MPI_REAL8,0,MPI_COMM_WORLD, ierr)call MPI_BARRIER(MPI_COMM_WORLD,ierr)
t2=MPI_WTIME()
if(myrank.eq.0) thenwrite(6,60) ' Execution Time = ',t2-t1,' sec',' A(n,n) = ',d(n,n)
endif
60 format(1x,a,f10.3,a,1x,a,d24.15)
call MPI_FINALIZE(ierr)stop
endPage 169