MPI「超」入門（C言語編）
東京大学情報基盤センター
FOTRAN編は以下
http://nkl cc u tokyo ac jp/seminars/T2Kfvm/MPIprogf pdf
http://nkl.cc.u-tokyo.ac.jp/seminars/T2Kfvm/MPIprogf.pdf

Fundamental MPI

1

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

Fundamental MPI

2

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

MPIフォーラム
MPIフラム
MPI-1規格MPI 2規格（拡張版） 現在はMPI 3が検討されている
MPI-2規格（拡張版），現在はMPI-3が検討されている• 実装– mpich アルゴンヌ国立研究所
ゴ
立 究
– LAM
– 各ベンダー
各ベ ダ– C/C++,FOTRAN,Java ; Unix，Linuｘ，Windows，Mac OS

Fundamental MPI

3

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

Fundamental MPI

4

参考文献• 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.
1998
• http://www-unix.mcs.anl.gov/mpi/www/– API（Application
API（A li i IInterface）の説明
f）の説明

Fundamental MPI

5

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

Fundamental MPI

6

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

Fundamental MPI

8

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

9

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

Fundamental MPI

10内 容• 環境管理• グループ通信– Collective Communication• 1対1通信– Point-to-Point
P i t t P i t Communication
Ci ti

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

11

Fundamental MPI

12

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

implicit REAL*8 (A-H,O-Z)
include 'mpif.h‘
i t
integer:: PETOTPETOT, my_rank,
k iierr
call 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 chello.c#include "mpi.h"
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);
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$
p 90 –Oss
Oss -noparallel
opa a e hello.f
e o.
“mpif90”：
FORTRAN90＋MPIによってプログラムをコンパイルする際に必 な
必要な，コンパイラ，ライブラリ等がバインドされている
パ
ブ
等がバ
ドされ
る

C言語
$> mpicc –Os
Os -noparallel
noparallel hello
hello.cc“mpicc”：
C＋MPIによってプログラムをコンパイルする際に必要な，コンパイラ，ライブラリ等がバインドされている
13

Fundamental MPI

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

Fundamental MPI

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

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

cd $PBS$PBS_O_WORKDIRO WORKDIR
mpirun numactl --localalloc ./a.out

実行ディレクトリ移動
mpirun15

Fundamental MPI

ジョブスクリプト（詳細）#@$-r hello#@$-q lecture#@$-N 1#@$-J T4#@$-e err#@$-o hello.lst#@$#@$-lM
28
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
t h
hello.lst
ll l tHello 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 –bqdel ジョブqde
ジョブ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
79880 batch1 submitted to queue: lecture
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 b t h1
79880.batch1
S1
S1-3
3
t15026l
lecturet00unlimitli it
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 を設定しておけばオプションを書かなくても自分のメールアドレスに送信できます• 結果の確認– 標準出力：– 標準エラー出力20

Fundamental MPI

21

環境管理ル チン＋必須項目
環境管理ルーチン＋必須項目
implicit REAL*8 (A-H,O-Z)
include 'mpif.h‘
integer :: PETOT, my_rank,
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_Init
初期化

MPI_Comm_size__
プロセス数取得
mpirun -np XX <prog>#include "mpi.h"#include <stdio<stdio.h>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 C
MPI_Comm_rankk
プロセスID取得
自分のプロセス番号（0から開始）

MPI_Finalize
MPIプロセス終了

Fundamental MPI

22

FORTRAN/Cの違い• 基本的にインタフェースはほとんど同じ
基本的にインタフェ スはほとんど同じ– Cの場合，「MPI_Comm_size」のように「MPI」は大文字，「MPI_」のあ
あとの最初の文字は大文字，以下小文字
最初 文
大文 ，以
文

• FORTRANはエラーコード（ierr）の戻り値を引数の最後に指FORTRANはエラ コ ド（ierr）の戻り値を引数の最後に指定する必要がある。
• 最初に呼ぶ「MPI_INIT」だけは違う– call MPI_INIT (ierr)– MPI_Init (int *argc, char ***argv)

Fundamental MPI

23

何をやっているのか ?
#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_size(MPI_COMM_WORLD,MPI_Comm_rank(MPI_COMM_WORLD, &myid);
printf ("Hello World %d¥n", myid);
MPI_Finalize();}#@$-r hello#@$-q lecture#@$ N 1#@$-N#@$-J T4#@$-e err#@$-o hello.lst#@$-lM 28GB#@$-lT#@$lT 00:05:00#@$

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

標準エラー出力ファイル名
標準出力ファイル名
1ノードあたりメモリ使用量（固定）
実行時間（上限15分 この場合は5分）
実行時間（上限15分，この場合は5分）

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

Fundamental MPI

24

mpi.h mpif.h
mpi.h，mpif.h
implicit REAL*8 (A-H,O-Z)
include 'mpif.h‘
integer :: PETOT, my_rank,
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<stdio.h>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サ
ブルーチンの引数として使用する以外は陽に値を変更してはいけない。
ユーザーは「MPI_」で始まる変数を独自に設定しないのが無難。

Fundamental MPI

25

MPI Init
MPI_Init•

MPIを起動する 他のMPI関数より前にコールする必要がある（必須）MPIを起動する。他のMPI関数より前にコルする必要がある（必須）• MPI_Init (argc, argv)#include "mpi.h"#include <stdio.h>
int main(int argc
argc, char **argv){
int n, myid, numprocs, i;
MPI_Init(&argc,&argv);
MPI
Init(&argc &argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
printf (("Hello
Hello World %d¥n%d¥n", myid);
MPI_Finalize();}Fundamental MPI

26

MPI Finalize
MPI_Finalize••

MPIを終了する。他の全てのMPI関数より後にコ
MPIを終了する
他の全てのMPI関数より後にコールする必要がある（必須）
ルする必要がある（必須）。
これを忘れると大変なことになる。
– 終わったはずなのに終わっていない・・・• MPI_Finalize ()#include "mpi.h"#include <stdio.h>
int main(int argc
argc, char **argv){
int n, myid, numprocs, i;
MPI_Init(&argc,&argv);
MPI
Init(&argc &argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
printf (("Hello
Hello World %d¥n%d¥n", myid);
MPI_Finalize();}Fundamental MPI

27

MPI Comm size
MPI_Comm_size•

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

• MPI_Comm_sizei((comm, size)i )– comm– size整数
整数

IO

コミュニケータを指定する
comm.で指定されたグループ内に含まれるプロセス数の合計#include "mpi.h"#include <stdio.h>
int main(int argc
argc, char **argv){
int n, myid, numprocs, i;
MPI_Init(&argc,&argv);
MPI
Init(&argc &argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
printf (("Hello
Hello World %d¥n%d¥n", myid);
MPI_Finalize();}Fundamental MPI

28

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

Fundamental MPI

29

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

COMM_CRUSTCOMM VIS
COMM_VIS

Fundamental MPI

30

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

動画

Fundamental MPI

31

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

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

Fundamental MPI

32

地盤，タンク デル
地盤，タンクモデル• 地盤モデル：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に配置
60 間隔で4×4に配置• 合計自由度数：2,918,169

Fundamental MPI

33

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

basement#2

basememt#033

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

Fundamental MPI

34

MPI Comm rank
MPI_Comm_rank•

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

• MPI_Comm_rank (comm, rank)– comm– rank

整数
整数

I
O

コミュニケータを指定する
comm.で指定されたグループにおけるプロセスID
0から始まる（最大はPETOT-1）#include "mpi.h"#include <stdio.h>
int main(int argc
argc, char **argv){
int n, myid, numprocs, i;
MPI_Init(&argc,&argv);
MPI
Init(&argc &argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
printf (("Hello
Hello World %d¥n%d¥n", myid);
MPI_Finalize();}Fundamental MPI

35

MPI Abort
MPI_Abort•

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

• MPI_Abort (comm, errcode)– comm– errcode

整数
整数

I
O

コミュニケータを指定する
ミ
ケ タを指定する
エラーコードFundamental MPI

36

MPI Wtime
MPI_Wtime•

時間計測用の関数：精度はいまいち良くない（短い時間の場合）• time= MPI_Wtime ()– timeR8…
double Stime, Etime;
Stime= MPI_Wtime ();(…)
Etime= MPI_Wtime ();O

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

37

MPI Wtime の例
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
11.117188E+00
117188E+00
1.117188E+00

プロセス
プ
セ
番号

計算時間

Fundamental MPI

38

MPI Wtick
MPI_Wtick••

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

implicit REAL*8 (A-H,O-Z)
include 'mpif.h'…
TM= MPIMPI_WTICKWTICK ()
write (*,*) TM…

時間計測精度（単位：秒）
double Time;…
Time = MPI_Wtick();
printf("%5d%16
printf(%5d%16.6E¥n
6E¥n", MyRank,
MyRank Time);…

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

39

Fundamental MPI40

MPI Barrier
MPI_Barrier••

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

• MPI_Barrier (comm)– comm

整数

I

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

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

41

Fundamental MPI

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

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

Fundamental MPI

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

43

Fundamental MPI

44

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

PE #0

PE #1

PE #2

PE #3

Program

Program

Program

ProgramData #0

Data #1

Data #2

Data #3

Fundamental MPI

45

SPMDに適したデ タ構造（1/2）
SPMDに適したデータ構造（1/2）• 大規模なデータ領域を分割して，各プロセッサ，プロセス
規で計算するのがSPMDの基本的な考え方
例えば長さNg( 20)の クトルVgに対して以下のような計• 例えば長さNg(=20)のベクトルVgに対して以下のような計算を考えてみよう：
int main(){
int i,Ng;double Vg[20];
Ng 20;
Ng=20;
for(i=0;i<Ng;i++){Vg[i] = 2.0*Vg[i];}return 0;}• これを4つのプロセッサで分担して計算するとすれば，れをッサ 分担計算する すれ ，
20/4=5 ずつ記憶し，処理すればよい。

Fundamental MPI

46

SPMDに適したデ タ構造（2/2）
SPMDに適したデータ構造（2/2）• すなわち，こんな感じ：
int main(){
int i,Nl;d bl Vl[5];doublel[5]Nl=5;for(i=0;i<Nl;i++){Vl[i] = 2.0*Vl[i];}return 0;}• このようにすれば「一種類の」プログラム（Single Program）
で並列計算を実施できる。
– 各プロセスにおいて，「Vl」の中身が違う：Multiple Data
– 可能な限り計算を「Vl」のみで実施することが，並列性能の高い計算へつながる。
– 単体CPUの場合ともほとんど変わらない。

Fundamental MPI

47

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

Fundamental MPI

局所デ タの考え方
局所データの考え方「全体データ」Vgの：g• 0～4番成分が0番PE• 5~9番成分が1番PE• 10～14番が2番PE
が• 15～19番が3番PEのそれぞれ，「局所デー
のそれぞれ
「局所デー
タ」Vlの0番～4番成分となる（局所番号が0番～4番となる）。

Vg[ 0]
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]

PE#0

Vl[0]
Vl[1]
Vl[2]
Vl[3]
Vl[4]

PE#1

Vl[0]
Vl[1]
Vl[2]
Vl[3]
[ ]
Vl[4]

PE#2

Vl[0]
Vl[1]
Vl[2]
Vl[3]
Vl[4]

PE#3

Vl[0]
Vl[1]
Vl[2]
Vl[3]
Vl[4]

48

Fundamental MPI

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

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

50

Fundamental MPI

51

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

Fundamental MPI

52

グル プ通信の例（1/4）
グループ通信の例（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Fundamental MPI

53

グル プ通信の例（2/4）
グループ通信の例（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

Fundamental MPI

54

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

P#0 A0 B0 C0 D0P#1 A1 B1 C1 D1P#0 op.A0-A3
op A0 A3 op.B0-B3
op B0 B3 op.C0-C3
op C0 C3 op.D0-D3
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

Fundamental MPI

55

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

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

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

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

Fundamental MPI

56

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

57

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

58

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

領域分割
大規模
データ

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

局所
データ

局所
データ
通信

局所
データ

局所
データ
Fundamental MPI

59

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

Fundamental MPI

Fundamental MPI

60

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

2
2
2
2
2
2

VECs[ 0]=
[ 1]=
[ 2]=
…
[17]=
[18]=
[19]=

3
3
3
3
3
3

Fundamental MPI

61<$FVM>/S1/dot.f, dot.c
implicit REAL*8 (A-H,O-Z)real(kind=8),dimension(20)::l(ki d 8) dii (20)&
VECp, VECs
do i= 1, 20
VECp(i)=
( ) 2
2.0d0
0 0
VECs(i)= 3.0d0
enddo#include <stdio.h>
i t main(){
int
i (){
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)enddosum = 0.0;
for(i=0;i<20;i++){(){
sum += VECp[i] * VECs[i];}return 0;

stop
end
}

Fundamental MPI

62<$FVM>/S1/dot.f, dot.cの実行>$ cd <$FVM>/S1>$ cc -O3 dot.c>$ f90 –O3 dot.f>$ ./a.out
/
t
1
2
3

2.
2.
2.

3.
3.
3.

18
19
20

2.
2
2.
2.

3.
3
3.
3.

…

dot product120.

Fundamental MPI63

MPI Reduce
MPI_Reduce•

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

P#0 A0 B0 C0 D0P#1 A1 B1 C1 D1Reduce

P#1

P#2 A2 B2 C2 D2

P#2

P#3 A3 B3 C3 D3

P#3

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

I
O

– count
整数– datatype 整数

II– sendbuf– recvbuf
b f

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
コミュニケータを指定する

Fundamental MPI

64

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

Fundamental MPIMPI Reduceの例（1/2）
MPI_Reduceの例（1/2）
MPI_Reduce((sendbuf,recvbuf,count,datatype,op,root,comm)
db f
b f
d
)

double X0, X1;
MPI_Reduce(&X0, &X1, 1, MPI_DOUBLE, MPI_MAX, 0, <comm>);double X0[4],X0[4] XMAX[4];MPI_Reduce(&X0, &XMAX, 4, MPI_DOUBLE, MPI_MAX, 0, <comm>);

各プロセスにおける，X0[i]の最大値が0番プロセスのXMAX[i]に入る（i=0~3）

65

Fundamental MPIMPI Reduceの例（2/2）
MPI_Reduceの例（2/2）
MPI_Reduce((sendbuf,recvbuf,count,datatype,op,root,comm)
db f
b f
d
)
double X0
X0, XSUM;
MPI_Reduce(&X0, &XSUM, 1, MPI_DOUBLE, MPI_SUM, 0, <comm>)

各プロセスにおける，X0の総和が0番PEのXSUMに入る。
double X0[4];MPI_Reduce(&X0[0], &X0[2], 2, MPI_DOUBLE_PRECISION, MPI_SUM, 0, <comm>)

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

66

Fundamental MPI

67
P#0 A0 B0 C0 D0MPI Bcast
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」にメッセージを送信。

• MPI_Bcast (buffer,count,datatype,root,comm)– buffer

任意

I/O
/

– count

整数I

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

I

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

FORTRANC– root– commMPI_INTEGER, MPI_REAL, MPI_DOUBLE_PRECISION, MPI_CHARACTER etc.
MPI_INT, MPI_FLOAT, MPI_DOUBLE, MPI_CHAR etc.

整数
整数

I
I

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

Fundamental MPI68

MPI Allreduce
MPI_Allreduce••

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

P#0 A0 B0 C0 D0P#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

MPI_Reduce
MPI
Reduce + MPI_Bcast
MPI Bcast
総和，最大値を計算したら，各プロセスで利用したい場合が多い• call MPI_Allreduce(sendbuf,recvbuf,count,datatype,op, comm)
任意
任意

I
O

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

I
I
I
I

– sendbuf– recvbuf––––

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

69

MPI Reduce/Allreduceの “op”
MPI_Reduce/Allreduceの
op
MPI_Reduce
MPI
Reduce(sendbuf,recvbuf,count,datatype,op,root,comm)• MPI_MAX，MPI_MIN
MPI MAX MPI MIN• MPI_SUM，MPI_PROD_• MPI_LAND

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

Fundamental MPI

Fundamental MPI

70

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

2
2
2
2
2
2
3
3
3
3
3
3

Fundamental MPI

71

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

VECp[0]= 2[1]= 2[2]= 2[3]= 2[4]= 2

VECs[0]= 3[1]= 3[2]= 3[3]= 3[4]= 3

PE#1

VECp[0]= 2[1]= 2[2]= 2[3]= 2[4]= 2

VECs[0]= 3[1]= 3[2]= 3[3]= 3[4]= 3

PE#2

VECp[0]= 2[1]= 2[2]= 2[3]= 2[4]= 2

VECs[0]= 3[1]= 3[2]= 3[3]= 3[4]= 3

PE#3

VECp[0]= 2
[1] 2
[1]=
[2]= 2[3]= 2[4]= 2

VECs[0]= 3
[1] 3
[1]=
[2]= 3[3]= 3[4]= 3

VECp[15]~VECp[19]
VEC [15] VEC [19]VECs[15]~VECs[19]

72

とは言え
とは言え・・・
Vg[ 0]• 全体を分割して，1から番全体を分割して 1から番
Vg[ 1]
Vg[ 2]
号をふり直すだけ・・・といVg[ 3]
4]
うのはいかにも簡単である Vg[
うのはいかにも簡単である。
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]

PE#0

Vl[0]
Vl[1]
Vl[2]
Vl[3]
Vl[4]

PE#1

Vl[0]
Vl[1]
Vl[2]
Vl[3]
[ ]
Vl[4]

PE#2

Vl[0]
Vl[1]
Vl[2]
Vl[3]
Vl[4]

PE#3

Vl[0]
Vl[1]
Vl[2]
Vl[3]
Vl[4]

Fundamental MPI

Fundamental MPI

73

内積の並列計算例（１/2）<$FVM>/S1/allreduce.c#include <stdio.h>#include <stdlib.h>#include "mpi.h"
int main(int argc
argc, char **argv){
int i,N;
int PeTot, MyRank;
double VECp[5], VECs[5];double sumA, sumR, sum0;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &PeTot);
MPI_Comm_rank(MPI_COMM_WORLD, &MyRank);
sumA= 0.0;
0 0;sumR= 0.0;
N=5;for(i=0;i<N;i++){VECp[i] = 2
2.0;0;VECs[i] = 3.0;}

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

sum0 = 0.0;
for(i=0;i<N;i++){sum0 += VECp[i] * VECs[i];}Fundamental MPI

74

内積の並列計算例（2/2）<$FVM>/S1/allreduce.c
MPI R dMPI_Reduce(&sum0,(&
0 &sumR,&
R 1,
1 MPI_DOUBLE,MPI DOUBLE MPI_SUM,
MPI SUM 0,
0 MPI_COMM_WORLD);MPI COMM WORLD)MPI_Allreduce(&sum0, &sumA, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);

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

Fundamental MPI

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

1.200000E+02
11.200000E+02
200000E+02

before BCAST
after
f
BCAST

1
1

1.200000E+02
11.200000E+02
200000 02

0.000000E+00
11.200000E+02
200000 02

before BCAST
after BCAST

33

1.200000E+02
1.200000E+02

0.000000E+00
1.200000E+02

before BCAST
after BCAST

22

11.200000E+02
200000E+02
1.200000E+02

00.000000E+00
000000E+00
1.200000E+02

Fundamental MPI

76

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

77

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

do i= 1,
1 NG
VECg(i)= VECg(i) + ALPHA
enddo

for (i=0; i<NG; i++{VECg[i]= VECg[i] + ALPHA}

Fundamental MPI

78

全体デ タと局所デ タ（2/3）
全体データと局所データ（2/3）• 簡単のために，– NG=32– ALPHA=1000.
– MPIプロセス数=4• ベクトルVECgとして以下のような32個の成分を持つベクトルを仮定する（<$FVM>/mpi/a1x.all）：(101.0, 103.0, 105.0, 106.0, 109.0, 111.0, 121.0, 151.0,
201 0 203.0,
201.0,
203 0 205.0,
205 0 206.0,
206 0 209.0,
209 0 211.0,
211 0 221.0,
221 0 251.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)

Fundamental MPI

79

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

計算手順
①

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

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

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

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

Fundamental MPI

80

Scatter/Gatherの計算 （1/8）
長さ32のベクトルVECgをあるプロセス（例えば0番）で読み込む。
• プロセス0番から「全体データ」を読み込む
プ セス0番から「全体デ タ を読み込む
include'mpif.h'integer, parameter :: NG= 32
real(kind=8),l(ki d 8) dimension(NG)::dii (NG) VECg
VEC
call MPI_INIT (ierr)call MPI_COMM_SIZE (<comm>, PETOT , ierr)call
ll MPI_COMM_RANKMPI COMM RANK (<comm>,(<> my_rank,k ierr)i )if (my_rank.eq.0) thenopen (21, file= 'a1x.all', status= 'unknown')
do i= 1, NG
read (21,*) VECg(i)enddoclose (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)(! y) for(i=0;i<NG;i++){(){
fscanf(fp, "%lf", &VECg[i]);}Fundamental MPI

81

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

Fundamental MPI

82

P#0 A0 B0 C0 D0MPI Scatter
MPI_Scatter

P#1
P#2
P#3

•

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

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

• MPI
MPI_Scatter
Scatter (sendbuf(sendbuf, scountscount, sendtype
sendtype, recvbuf
recvbuf, rcountrcount,recvtype, root, comm)
–
–
–
–
–
–
–
–

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

I
I
I
O
I
I
I
I

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

Fundamental MPI

83

MPI_Scatter（続き）

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

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

•

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

I
I
I
O
I
I
I
I

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

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

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

Fundamental MPI

84

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,
db f scount,t sendtype,dt
recvbuf,b f rcount,t
recvtype, root, comm)Fundamental MPI

85

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

8

8

8

8

PE#0

PE#1

PE#2

PE#3

8

8

8
VECg
sendbuf

8

局所デ タ
局所データ
local data

全体データ
全体デ
タ
root global data

Fundamental MPI

86

Scatter/Gatherの計算 （5/8）
4つのプロセスへ均等に（長さ8ずつ）割り振る。
• 全体デ
全体データ（globalタ（data）としてはVECgの1番から32番までの要素番号）とし は番から 番ま要素番号を持っていた各成分が，それぞれのプロセスにおける局所データ（local
data）としては VECの1番から8番までの局所番号を持った成分として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]);}Fundamental MPI

87

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

PE#0
before 0 1
before 0 2
before 0 3
before 0 4
before 0 5
before 0 6
before 0 7
before 0 8

101.
101
103.
105.
106.
109
109.
111.
121.
151.

PE#1
before 1 1
before 1 2
before 1 3
before 1 4
before 1 5
before 1 6
before 1 7
before 1 8

201.
201
203.
205.
206.
209
209.
211.
221.
251.

PE#2
before 2 1
before 2 2
before 2 3
before 2 4
before 2 5
before 2 6
before 2 7
before 2 8

301.
301
303.
305.
306.
309
309.
311.
321.
351.

PE#3
before 3 1
before 3 2
before 3 3
before 3 4
before 3 5
before 3 6
before 3 7
before 3 8

401.
401
403.
405.
406.
409
409.
411.
421.
451.

Fundamental MPI

88

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
after 0 2
after 0 3
after 0 4
after 0 5
after 0 6
after 0 7
after 0 8

1101.
1103.
1105
1105.
1106.
1109.
1111.
1121
1121.
1151.

PE#1
after 1 1
after 1 2
after 1 3
after 1 4
after 1 5
after 1 6
after 1 7
after 1 8

1201.
1203.
1205
1205.
1206.
1209.
1211.
1221
1221.
1251.

PE#2
after 2 1
after 2 2
after 2 3
after 2 4
after 2 5
after 2 6
after 2 7
after 2 8

1301.
1303.
1305
1305.
1306.
1309.
1311.
1321
1321.
1351.

PE#3
after 3 1
after 3 2
after 3 3
after 3 4
after 3 5
after 3 6
after 3 7
after 3 8

1401.
1403.
1405
1405.
1406.
1409.
1411.
1421
1421.
1451.

Fundamental MPI

89

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

90

P#0 A0 B0 C0 D0MPI Gather
MPI_Gather

P#1
P#2
P#3

•

P#0 A0
Scatter
GatherP#1 B0P#2 C0P#3 D0MPI_Scatterの逆• MPI
MPI_Gather
Gather (sendbuf(sendbuf, scountscount, sendtype
sendtype, recvbuf
recvbuf, rcountrcount,recvtype, root, comm)
–
–
–
–
–
–
–
–

•

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

I
I
I
O
I
I
I
I

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

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

Fundamental MPI

91

Scatter/Gatherの計算 （8/8）
各プロセスの結果を再び長さ32のベクトルにまとめる• 本例題の場合
本例題の場合，root=0として，各プロセスから送信されるVECの成分を0
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
recvbuf

8

局所デ タ
局所データ
local data

全体データ
全体デ
タ
root global data

Fundamental MPI

92<$FVM>/S1/scatter-gather.c
実行例$> mpicc
i–Os
O -noparallel
ll l scatter-gather.cttth$> mpif90 –Oss -noparallel scatter-gather.fg$> 実行（4プロセス） go4.sh
PE#0
before 0 1
before 0 2
before 0 3
before 0 4
before 0 5
before 0 6
before 0 7
before 0 8

101.
103
103.
105.
106.
109.
111.
121
121.
151.

PE#1
before 1 1
before 1 2
before 1 3
before 1 4
before 1 5
before 1 6
before 1 7
before 1 8

201.
203
203.
205.
206.
209.
211.
221
221.
251.

PE#2
before 2 1
before 2 2
before 2 3
before 2 4
before 2 5
before 2 6
before 2 7
before 2 8

301.
303
303.
305.
306.
309.
311.
321
321.
351.

PE#3
before 3 1
before 3 2
before 3 3
before 3 4
before 3 5
before 3 6
before 3 7
before 3 8

401.
403.
403
405.
406.
409.
411.
421
421.
451.

PE#0
after 0 1
after 0 2
after 0 3
after 0 4
after 0 5
after 0 6
after 0 7
after 0 8

1101.
1101
1103.
1105.
1106.
1109.
1111
1111.
1121.
1151.

PE#1
after 1 1
after 1 2
after 1 3
after 1 4
after 1 5
after 1 6
after 1 7
after 1 8

1201.
1201
1203.
1205.
1206.
1209.
1211
1211.
1221.
1251.

PE#2
after 2 1
after 2 2
after 2 3
after 2 4
after 2 5
after 2 6
after 2 7
after 2 8

1301.
1301
1303.
1305.
1306.
1309.
1311
1311.
1321.
1351.

PE#3
after 3 1
after 3 2
after 3 3
after 3 4
after 3 5
after 3 6
after 3 7
after 3 8

1401.
1401
1403.
1405.
1406.
1409.
1411
1411.
1421.
1451.

Fundamental MPI

93

MPI Reduce scatter
MPI_Reduce_scatterP#0 op.A0-A3

P#0 A0 B0 C0 D0P#1 A1 B1 C1 D1•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• MPI
MPI_Reduce_Scatter
Reduce Scatter (sendbuf, recvbuf, rcount, datatype,op, comm)
–
–
–
–
–
–

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

I
O
I
I
I
I

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

Fundamental MPI

94

P#0 A0MPI Allgather
MPI_Allgather•

P#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する（各プロセスで同じデータを持つ）• MPI_Allgather (sendbuf, scount, sendtype, recvbuf, rcount,recvtype, comm)
–
–
–
–
–
–
–

sendbuf 任意
整数
scountsendtype 整数
recvbuf 任意
整数
rcountrecvtype 整数
comm
整数

I
I
I
O
I
I
I

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

Fundamental MPI

95

MPI Alltoall
MPI_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の更なる拡張：転置• MPI_Alltoall (sendbuf, scount, sendtype, recvbuf, rcount,recvrype, comm)
–
–
–
–
–
–
–

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

I
I
I
O
I
I
I

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

Fundamental MPI

96

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

97

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

Fundamental MPI

98

分散ファイル読み込み：等デ タ長（1/2）
分散ファイル読み込み：等データ長（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
Oss -noparallel
noparallel file.f
file f>$ 実行：4プロセス go4.sh

Fundamental MPI

99

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

a1.0

a1 1
a1.1

a1.2

a1.3

Fundamental MPI

100

分散ファイル読み込み：等デ タ長（2/2）
分散ファイル読み込み：等データ長（2/2）<$FVM>/S1/file.c
int main(int argc
argc, char **argv){argv){
int i;
int PeTot, MyRank;
MPI_Comm SolverComm;
double vec[8];char FileName[80];FILE *fp;

Hello とそんなに変わらない

MPI_Init(&argc, &argv);
MPI Comm size(MPI COMM WORLD &PeTot);
MPI_Comm_size(MPI_COMM_WORLD,MPI_Comm_rank(MPI_COMM_WORLD, &MyRank);
sprintf(FileName, "a1.%d", MyRank);
fp = fopen(FileName, "r");if(fp == NULL) MPI_Abort(MPI_COMM_WORLD, -1);「局所番号（0~7）」でfor(i=0;i<8;i++){
読み込む
fscanf(fp, "%lf", &vec[i]);}for(i=0;i<8;i++){printf("%5d%5d%10.0f¥n", MyRank, i+1, vec[i]);}MPI Finalize();
MPI_Finalize();
return 0;}

Fundamental MPI

101

SPMDの典型例

PE #0

PE #1

PE #2

PE #3

“a.out”“a.out”“a.out”“a.out”

“a1
a1.0
0”

“a1
a1.1
1”

“a1
a1.2
2”

“a1
a1.3
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
a2 05
各PEにおける成分数
201.0
成分の並び
203.0
205.0
206 0
206.0
209.0
>$
$ mpicci–Os
O -noparallel
ll l filfile2.c2>$ mpif90 –Oss -noparallel file2.f>$ 実行：4プロセス go4.sh

102

Fundamental MPI

103

分散ファイルの読み込み：可変長（2/2）<$FVM>/S1/file2.c
int main(int argc,
g , char **argv){g
int i, int PeTot, MyRank;
MPI_Comm SolverComm;
double *vec, *vec2, *vecg;
int num;d bl sum0,
double0 sum;
char filename[80];FILE *fp;
MPI_Init(&argc,
MPI
Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &PeTot);
MPI_Comm_rank(MPI_COMM_WORLD, &MyRank);
sprintf(filename, "a2.%d", MyRank);
fp = fopen(filename, "r");assert(fp != NULL);
fscanf(fp, "%d", &num);
vec = malloc(num * sizeof(double));for(i=0;i<num;i++){fscanf(fp, "%lf", &vec[i]);}for(i=0;i<num;i++){printf("p( %5d%5d%5d%10.0f¥n",, MyRank,y, i+1,, num,, vec[i]);}[ ]);}
MPI_Finalize();}Fundamental MPI

局所データの作成法• 全体データ（N=NG）を入力– Scatterして各プロセスに分割
S tt して各プロセスに分割– 各プロセスで演算– 必要に応じて局所データをGather（またはAllgather）して全体デー
必要に応じて局所デ タをGather（またはAllgather）して全体デ
タを生成

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

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

105

Fundamental MPI

106

有限体積法：全体マトリクスの生成
要素iに関する釣り合い
⎡
⎤
⎡
⎤
⎢
⎢
⎥
Sie
Sik
Sik
Sie ⎥
&
⎥Ti − ⎢⎢+
TiBe
Tk ⎥ =
Sid q&id + Vi Qi +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

107

前処理付き共役勾配法
Preconditioned Conjugate Gradient Method （CG）
0 = b-[A]x(0)0Compute r(0)for i= 1, 2, …
solve [M]z(i-1)= r(i-1)(i 1) z(i-1)(i 1)
ρi-1= r(i-1)
if i=1p(1)= z(0)else
βi-1= ρi-1/ρi-2p(i)= z(i-1) + βi-1 p(i)endif
q(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

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

1対1通信とは ?
• グループ通信：Collective Communication– MPI_Reduce,
MPI R dMPIMPI_Scatter/Gather
S tt /G th など– 同じコミュニケータ内の全プロセスと通信する– 適用分野• 境界要素法，スペクトル法，分子動力学等グローバルな相互作用のある手法• 内積，最大値などのオペレーション
内積 最大値などのオペレーション #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

108

Fundamental MPI

1対1通信の方法• MPI_Send，
MPI Send MPI_Recvというサブルーチンがある。
MPI Recvというサブルーチンがある• しかし，これらは「ブロッキング（blocking）」通信サブルーチンで デ ドロ ク（d d llock）を起こしやすい。
で，デッドロック（deadk）を起こしやすい– 受信（Recv）の完了が確認されないと，送信（Send）が終了しない• もともと非常に「secureな」通信を保障するために，MPI仕様の中に入れられたものであるが，実用上は不便この上ない。
– したがって実際にアプリケーションレベルで使用されることはほとん
ど無い（と思う）。
– 将来にわたってこの部分が改正される予定はないらしい。

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

Fundamental MPIMPI Send/MPI Recv
MPI_Send/MPI_Recv
PE#0
1

2

3

if (
(my_rank.eq.0)k0) NEIB
NEIB_ID=1
ID 1
if (my_rank.eq.1) NEIB_ID=0

4

5

…
_
(NEIB_ID,_
arg’s)
call MPI_Send
call MPI_Recv (NEIB_ID, arg’s)
…

PE#1
4

1

2

3

4

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

Fundamental MPIMPI Send/MPI Recv （続き）
MPI_Send/MPI_Recv
if ((myy_rank.eq.0)q ) NEIB_ID=1
if (my_rank.eq.1) NEIB_ID=0

PE#0
1

2

3

4

…
if (my_rank.eq.0) thencall MPIMPI_Send
Send (NEIB_ID,(NEIB ID arg’s)
arg s)
call MPI_Recv (NEIB_ID, arg’s)
endif

5

PE#1
4

1

2

3

4

if (my_rank.eq.1) thencall
ll MPIMPI_Recv
R(NEIB(NEIB_ID,
ID arg’s)’ )call MPI_Send (NEIB_ID, arg’s)
endif…

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

111

Fundamental MPI

1対1通信の方法（実際どうするか）• MPI_Isend，
MPI Isend MPI_Irecv，という「ブロッキングしない
MPI Irecv という「ブロッキングしない（non-blocking）」サブルーチンがある。これと，同期のための「MPI Waitall」を組み合わせる「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です（後述）112

Fundamental MPIMPI Isend
MPI_Isend•

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

• MPI_Isend(sendbuf,count,datatype,dest,tag,comm,request)
–
–
–
–
–

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

I
I
I
I
I

整数
整数

I
O

– comm– request

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

113

Fundamental MPI

通信識別子（request handle）： request• MPI_Isend(sendbuf count datatype dest tag comm request)(sendbuf,count,datatype,dest,tag,comm,request)
–
–
–
–
–

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

I
I
I
I
I

整数
整数

I
O

– comm– request•

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

114

Fundamental MPIMPI Irecv
MPI_Irecv•

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

• MPI_Irecv(recvbuf,count,datatype,dest,tag,comm,request)
–
–
–
–
–

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

I
I
I
I
I

整数
整数

I
O

– comm– request

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

115

Fundamental MPIMPI Waitall
MPI_Waitall•••

1対1非ブロッキング通信関数である「MPI_Isend」と「MPI_Irecv」を使用した場合，プロセスの同期を取るのに使用する。
ロセスの同期を取るのに使用する
送信時はこの「MPI_Waitall」を呼ぶ前に送信バッファの内容を変更してはならない。
受信時は「MPI_Waitall」を呼ぶ前に受信バッファの内容を利用してはならない。
整合性が取れていれば， 「MPI_Isend」と「MPI_Irecv」を同時に同期してもよい。
– 「MPI_Isend/Irecv」で同じ通信識別子を使用すること•「MPI Barrier」と同じような機能であるが 代用はできない「MPI_Barrier」と同じような機能であるが，代用はできない。
– 実装にもよるが，「request」，「status」の内容が正しく更新されず，何度も「MPI_Isend/Irecv」を呼び出すと処理が遅くなる，というような経験もある。

• MPI_Waitall(count,request,status)– count– request
t

整数
整数

I
I/O
/O

– status

整数O

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

Fundamental MPI

状況オブジェクト配列（status object）：
status• MPI_Waitall•(count,request,status)
(
, q
,
)

– count– request

整数
整数

I
I/O

– status

整数O– ierr

整数O

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

予め記憶領域を確保しておくだけでよい

117

Fundamental MPIMPI Sendrecv
MPI_Sendrecv•

MPI_Send+MPI_Recv• MPI_Sendrecv(sendbuf,sendcount,sendtype,dest,sendtag,recvbuf,
recvcount recvtype source recvtag comm status)
recvcount,recvtype,source,recvtag,comm,status)
–
–
–
–
–

sendbuf
sendcountsendtype
dest
sendtag

任意
整数
整数
整数
整数

I
I
I
I
I

–
–
–
–
–

recvbuf
recvcountrecvtypesourcesendtag
d

任意
整数
整数
整数
整数

I
I
I
I
I

–
–

comm
status整数
整数

IO

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

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

119

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

120

Fundamental MPI

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

121

Fundamental MPI

通信識別子，状況オブジェクト配列の定義の仕方（C） 特殊な変数 型がある
仕方（C）：特殊な変数の型がある• MPI
MPI_Isend:
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)
malloc(sizeof(MPI Request) * NEIBpetot);
RequestRecv = malloc(sizeof(MPI_Request) * NEIBpetot);• MPI_Sendrecv:
MPI Sendrecv: status
MPI_Status *Status;
・・・
Status = malloc(sizeof(MPI_Status));122

Fundamental MPI

利用例（1）：スカラ 送受信
利用例（1）：スカラー送受信• PE#0，PE#1間 で8バイト実数VALの値を交換する。
if (my_rank.eq.0) NEIB= 1
if (my_rank.eq.1) NEIB= 0,1,MPI_DOUBLE_PRECISION,NEIB,…,req_send,…)call MPI_Isend (VAL
callll MPIMPI_Irecv
I(VALtemp,1,MPI_DOUBLE_PRECISION,NEIB,…,req_recv,…)(VALt
1 MPI DOUBLE PRECISION NEIB)
call MPI_Waitall (…,req_recv,stat_recv,…):受信バッファ VALtemp を利用可能
call MPI_Waitall (…,req_send,stat_send,…)：送信バッファ VAL を変更可能
VAL= VALtemp
if (my_rank.eq.0) NEIB= 1
if (my_rank.eq.1) NEIB= 0
call MPIMPI_Sendrecv
Sendrecv (VAL
,1,MPI_DOUBLE_PRECISION,NEIB,…
1 MPI DOUBLE PRECISION NEIB
VALtemp,1,MPI_DOUBLE_PRECISION,NEIB,…, status,…)
VAL= VALtemp&

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

123

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

Fundamental MPIIsend/Irecv/Waitall$> cd <$FVM>/S2$> mpicc –Os -noparallel ex1-1.c$> mpif90p–Oss -noparallelp
ex1-1.f$> 実行（2プロセス） go2.sh#include <stdio.h>#include <stdlib.h>#include "mpi.h"
int main(int argc, char **argv){
int neib, MyRank, PeTot;
double VAL, VALx;
MPI_Status *StatSend, *StatRecv;
MPI_Request *RequestSend, *RequestRecv;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &PeTot);
MPI_Comm_rank(MPI_COMM_WORLD, &MyRank);
StatSend = malloc(sizeof(MPI_Status) * 1);
StatRecv = malloc(sizeof(MPI_Status) * 1);
RequestSend = malloc(sizeof(MPI_Request) * 1);
RequestRecv = malloc(sizeof(MPI_Request) * 1);
if(MyRank == 0) {neib= 1; VAL= 10.0;}i
if(MyRank== 11) {neib=i00; VAL= 11
11.0;}
0
MPI_Isend(&VAL , 1, MPI_DOUBLE, neib, 0, MPI_COMM_WORLD, &RequestSend[0]);MPI_Irecv(&VALx, 1, MPI_DOUBLE, neib, 0, MPI_COMM_WORLD, &RequestRecv[0]);MPI_Waitall(1, RequestRecv, StatRecv);
MPI_Waitall(1,i ll(1 RequestSend,d StatSend);d)
VAL=VALx;
MPI_Finalize();
return 0; }124

Fundamental MPI

利用例（1）：スカラー送受信 C
S dR
SendRecv$> cd <$FVM>/S2$> mpicc –Os -noparallel ex1-2.c$ mpif90 –Oss -noparallel ex1-2.f$>$> 実行（2プロセス） go2.sh#include <stdio.h>#i l d <stdlib.h>#include
tdlib h#include "mpi.h"
int main(int argc, char **argv){
int neib;
int MyRank, PeTot;d bl VAL
double
VAL, VALtemp;
VALt
MPI_Status *StatSR;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &PeTot);
MPI C
MPI_Comm_rank(MPI_COMM_WORLD,
k(MPI COMM WORLD &MyRank);&M R k)if(MyRank == 0) {neib= 1; VAL= 10.0;}
if(MyRank == 1) {neib= 0; VAL= 11.0;}
St tSR = malloc(sizeof(MPI_Status));
StatSRll ( if(MPI St t ))MPI_Sendrecv(&VAL, 1, MPI_DOUBLE, neib, 0,&VALtemp, 1, MPI_DOUBLE, neib, 0, MPI_COMM_WORLD, StatSR);
VAL=VALtemp;
MPI_Finalize();
return 0;}
125Fundamental MPI

利用例（2）：配列の送受信（1/4）• PE#0，PE#1間
PE#0 PE#1間 で8バイト実数配列VECの値を交換する。
で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）PE#0：VEV（12）~VEC（36）の値として受け取る
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
201,202,203,~,235,236
202 203 ~ 235 236• 次ページのような結果になることを確認せよ• 以下のそれぞれを使用したプログラムを作成せよ– MPI_Isend/Irecv/Waitall– MPI_Sendrecv
MPI S d127

Fundamental MPI予測される結果
0 #BEFORE# 1
0 #BEFORE# 2
0 #BEFORE# 3
0 #BEFORE# 4
0 #BEFORE# 5
0 #BEFORE# 6
0 #BEFORE# 7
0 #BEFORE# 8
0 #BEFORE# 9
0 #BEFORE# 10
0 #BEFORE# 11
0 #BEFORE# 12
0 #BEFORE# 13
0 #BEFORE# 14
0 #BEFORE# 15
0 #BEFORE# 16
0 #BEFORE### 17
0 #BEFORE# 18
0 #BEFORE# 19
0 #BEFORE# 20
0 #BEFORE# 21
0 #BEFORE# 22
0 #BEFORE# 23
0 #BEFORE# 24
0 #BEFORE# 25
0 #BEFORE# 26
0 #BEFORE# 27
0 #BEFORE# 28
0 #BEFORE# 29
0 #BEFORE# 30
0 #BEFORE# 31
0 #BEFORE# 32
0 #BEFORE# 33
0 #BEFORE# 34
0 #BEFORE# 35
0 #BEFORE# 36

101
101.
102.
103.
104.
105.
106
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122
122.
123.
124.
125.
126.
127
127.
128.
129.
130.
131.
132
132.
133.
134.
135.
136.

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

101.
101
102.
103.
104.
105.
106.
106
107.
108.
109.
110.
111.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
211
212.
213.
214.
215.
216.
216
217.
218.
219.
220.
221.
221
222.
223.
224.
225.

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

201
201.
202.
203.
204.
205.
206
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222
222.
223.
224.
225.
226.
227
227.
228.
229.
230.
231.
232
232.
233.
234.
235.
236.

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

201.
201
202.
203.
204.
205.
206
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222
222.
223.
224.
225.
101.
102
102.
103.
104.
105.
106.
107
107.
108.
109.
110.
111.

128

Fundamental MPI

利用例（2）：配列の送受信（2/4）
if (my_rank.eq.0) thencall MPIMPI_Isend
Isend (VEC( 1),11,MPI
1),11,MPI_DOUBLE_PRECISION,1,…,req_send,…)
DOUBLE PRECISION,1,…,req send,…)call MPI_Irecv (VEC(12),25,MPI_DOUBLE_PRECISION,1,…,req_recv,…)
endif
(my
y_rank.eq.1)q ) thenif (call 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=
start_send=1length_send= 11
start_recv= length_send + 1
length_recv= 25
endif
if (my_rank.eq.1) then
NEIB= 0start send= 1
start_send
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=
start_send=1length_send= 11
start_recv= length_send + 1
length_recv= 25
endif
if (my_rank.eq.1) then
NEIB= 0start send= 1
start_send
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
VEC(start_recv),length_recv,MPI_DOUBLE_PRECISION,NEIB,…,status )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」は送信側の「length send」と受信側の「length recv」は一致し致している必要がある。
– PE#0⇒PE#1，PE#1⇒PE#0
PE#0⇒PE#1 PE#1⇒PE#0• 「送信バッファ」と「受信バッファ」は別のアドレス

132

Fundamental MPI

解答例（1/3）
I
Isend/Irecv/Waitalld/I/W it ll$> cd <$FVM>/S2$> mpicc –Os
Os -noparallel
noparallel ex2a.c$> mpif90 –Oss -noparallel ex2a.f$> 実行（2プロセス） go2.sh#include <stdio.h>#include <stdlib.h>p#include “mpi.h”
int main(int argc, char **argv){
int i, neib;
int MyRank, PeTot;
double VEC[36];int Start_Send, Length_Send;
int Start_Recv, Length_Recv;
MPI_Status *StatSend, *StatRecv;
MPI Request *RequestSend,
MPI_Request*RequestSend *RequestRecv;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &PeTot);
MPI_Comm_rank(MPI_COMM_WORLD, &MyRank);

133

Fundamental MPI

解答例（2/3）
I
Isend/Irecv/Waitalld/I/W it ll
St t S d 1
Start_Send=1;
if(MyRank == 0) {neib= 1;
Length_Send=_
11;
Length_Recv= 25;
for (i=0;i<36;i++){VEC[i]=100+i+1;}}
if(MyRank == 1) {neib= 0;neib
Length_Send= 25;
Length_Recv= 11;
for (i=0;i<36;i++){VEC[i]=200+i+1;}}
Start Recv= 1 + Length
Start_Recv=
Length_Send;
Send;
StatSend = malloc(sizeof(MPI_Status) * 1);
StatRecv = malloc(sizeof(MPI_Status) * 1);
RequestSend = malloc(sizeof(MPI_Request) * 1);
RequestRecv = malloc(sizeof(MPI_Request)
i
* 1
1);
for (i=0;i<36;i++) {printf("%s%2d%5d%8.0f¥n", "### before", MyRank, i, VEC[i]);}134

Fundamental MPI

解答例（3/3）
I
Isend/Irecv/Waitalld/I/W it ll
MPI_Isend(&VEC[Start_Send-1], Length_Send, MPI_DOUBLE, neib, 0,
MPI_COMM_WORLD,, &RequestSend[0]);q[ ]);MPI_Irecv(&VEC[Start_Recv-1], Length_Recv, MPI_DOUBLE, neib, 0,
MPI_COMM_WORLD, &RequestRecv[0]);MPI Waitall(1 RequestRecv
MPI_Waitall(1,RequestRecv, StatRecv);
MPI_Waitall(1, RequestSend, StatSend);for (i=0;i<36;i++) {printf("%s%2d%5d%8.0f¥n",
i tf("% %2d%5d%8 0f¥ " "### after
ft
"
", M
MyRank,R k ii, VEC[i])VEC[i]);}}
MPI_Finalize();
return 0;}• 汎用性がある• 「データが全て」という気がして来ないだろうか?
「デ タが全て という気がして来ないだろうか?
135