MPI（片方向通信）

2019年3月15日
神戸大学大学院システム情報学研究科計算科学専攻
横川三津夫

分散メモリ型並列計算機
複数のプロセッサがネットワークで接続されており，れぞれのプロセッ
サ（PE）が，メモリを持っている．
各PEが自分のメモリ領域のみアクセス可能

PE0

PE1

PEnメモリ

メモリ

メモリ

インターコネクト・ネットワーク

特徴
数千から数万PE規模の並列システムが可能
PEの間のデータ分散を意識したプログラミングが必要．
プログラミング技術
メッセージ・パシング・インターフェイス（MPI）によるプログラミング2019/3/15

KOBE HPC Spring School 20191

MPIの実行モデル：SPMD（Single Program, Multiple Data)
複数のプロセスにより並列実行
実行開始から終了まで，全プロセスが同じプログラムを実行
各MPIプロセスは固有の番号（ランク番号）を持つ
P個のプロセスで実行する場合，プロセス番号は0から(P-1)
までの整数
各プロセスで処理を変えたいときは，ランク番号を使った分岐により，各プロセスの処理を記述する．
実行開始

rank=0
プロセスa.out

rank=1
プロセスrank=2
プロセス

a.out

rank=3
プロセス

a.out

並列実行部分

a.out
実行終了

2019/3/15

KOBE HPC Spring School 20192

MPIの実行モデル（続き）
メモリ空間
プロセスごとに独立したメモリ空間を保持プログラム中で定義された変数や配列は，同じ名前で独立に各プロセスのメモリ上に割り当てられる．
同じ変数や配列に対して，プロセスごとに違う値を与えることが可能
他のプロセスの持つ変数や配列には，直接にアクセスできない．
実行開始

rank=0プロセス

rank=1
プロセス

a.out
integer :: kreal(DP) :: x(100)k = 138x(62) =4.254

a.out
integer :: kreal(DP) :: x(100)

直接にアクセス不可能

k = 5478
x(62) = 2847.45

実行終了
2019/3/15

KOBE HPC Spring School 20193

MPIの実行モデル（続き）
プロセス間通信
他のプロセスの持つ変数や配列のデータにアクセスできない．
⇒ プロセス間通信によりデータを送ってもらう．
メッセージパッシング方式：メッセージ（データ）の送り手と受け手
この方式によるプロセス間通信関数の集合 ≒ MPI
実行開始

rank=0プロセス
データを送る（例：mpi_send)

rank=1
プロセスa.out
integer :: kreal(DP) :: x(100)MPI関数によるプロセス間通信
2019/3/15

KOBE HPC Spring School 2019

a.out
integer :: kreal(DP) :: x(100)

データを受取る（例：mpi_recv)
実行終了
4

SPMDのイメージ
それぞれの人が，本棚に一連のノートを持っている．
それぞれの人には，名前が付いている（人を区別できる）．
ノートには同じ名前が付けられているが，中身は違っている．

本棚のノートに対し，それぞれの人が，読んだり書いたり…
大体は同じ作業をするが，最初のノートの中身が違うので，中身はそれぞれ違う．
ある人に，他の人とは違う作業をさせた
い場合には，名前で作業と指示してあげる．

【必要な情報】
ノートの名前
何冊誰に荷物のタグなど

時々，他の人のノートを見たい．
相手にノートの中身を送ってあげる．
送られた人は，それを違う名前のノートに中身を書き写す．
2019/3/15

KOBE HPC Spring School 20195

MPIプログラムのスケルトン（C）#include <stdio.h>#include <mpi.h>

MPIモジュールの取り込み（おまじない1）

int main(int argc, char **argv){
int nprocs, myrank;

MPIで使う変数の宣言

MPI_Init( &argc, &argv );MPI_Comm_size( MPI_COMM_WORLD, &nprocs );MPI_Comm_rank( MPI_COMM_WORLD, &myrank );MPIの初期化（おまじない2）
MPIで使うプロセス数を nprocs に取得
自分のプロセス番号を myrank に取得(この部分に並列実行するプログラムを書く）
MPI_Finalize() ;return 0 ;MPIの終了処理（おまじない3）}

 それぞれのプロセスで異なった処理をする場合は，myrankの値で場合分けし，うまく仕事が振り分けられるようにする．
2019/3/15

Kobe HPC Spring School 20196

MPIプログラムのスケルトン（Fortran）
program mainuse mpi
implicit none
integer :: nprocs, myrank, ierr

MPIで使う変数の宣言

call mpi_init( ierr )call mpi_comm_size( MPI_COMM_WORLD, nprocs, ierr )call mpi_comm_rank( MPI_COMM_WORLD, myrank, ierr )MPIの初期化（おまじない2）
MPIで使うプロセス数を nprocs に取得
自分のプロセス番号を myrank に取得

MPIモジュールの取り込み（おまじない1）(この部分に並列実行するプログラムを書く）

call mpi_finalize( ierr )MPIの終了処理（おまじない3）

end program main それぞれのプロセスが何の計算をするかは，myrankの値で場合分けし，うまく仕事が振り分けられるようにする．

2019/3/15

KOBE HPC Spring School 20197

講義の内容メッセージ・パシング・インターフェイス（MPI）
双方向通信
片方向通信
演習問題

2019/3/15

KOBE HPC Spring School 20198

メッセージ・パシング・インターフェイス
Message Passing Interface (MPI) とは．．．
複数の独立したプロセス間で，並列処理を行うためのプロセス間メッセージ通信の標準規格
1992年頃より米国の計算機メーカ，大学などを中心に標準化
MPI規格化の歴史
1994 MPI-1
1997 MPI-2（一方向通信など）
2012 MPI-3
http://www.mpi-forum.org/docs/mpi-3.0/mpi30-report.pdf

2019/3/15

KOBE HPC Spring School 20199

双方向通信（one-to-one communication）
送信側と受信側で，対応する関数を呼ぶ．
MPI_send，MPI_recvなどの組合せ
実行開始

rank=0プロセス
データを送る（例：mpi_send)

rank=mプロセス

a.out
integer :: kreal(DP) :: x(100)MPI関数によるプロセス間通信

2019/3/15

KOBE HPC Spring School 2019

a.out
integer :: kreal(DP) :: x(100)

データを受取る（例：mpi_recv)
実行終了

10

片方向通信（One-sided communication）
通信相手の状態に関係なく，他のプロセスのデータをアクセスする通信方法
Get
相手のプロセスのデータを獲得
Put
相手のプロセスにデータを挿入
実行開始

rank=nプロセス

他のプロセスのデータにアクセスするGet

rank=m
プロセス

Put
実行終了

2019/3/15

KOBE HPC Spring School 201911

片方向通信の利点
プロセス間の同期待ちを削減

性能向上の可能性
データコピーの回数を削減

双方向通信は，データの中間バッファを用いた実装
RDMA（Remote Direct Memory Access）機構を持つシステム
では，高速実行が可能となる．
計算とデータ通信のオーバーラップ
大きなデータ通信において高速実行される場合がある．

2019/3/15

KOBE HPC Spring School 201912

Windowオブジェクトの生成【C】int MPI_Win_create(void *base, MPI_Aint size, int disp_unit,
MPI_Info info, MPI_Comm comm, MPI_Win *win )【F】mpi_win_create( base, size, disp_unit, info, comm, win, ierr )RMA操作に必要なWindowオブジェクトを生成する．
[Input]base:windowの先頭アドレス
size:
windowのサイズ（整数型，バイト単位で指定）
Fortranの場合，integer(kind=MPI_ADDRESS_KIND):: size とする．
disp_unit: ずれ当りのサイズ（整数型，バイト単位で指定）
MPI_INTEGER，MPI_REALなどではない．MPI_Get, MPI_Putなどで使用する．
info:
情報（Fortranでは整数型，CではMPI_Info型）
infoは今回は使わないので，MPI_INFO_NULL とすればよい．
comm:コミュニケータ（MPI_COMM_WORLDなど）[Output]
win:

Windowオブジェクト（Fortranでは整数型，CではMPI_Win型）

MPI_get，MPI_Putなどで利用

ierr:
2019/3/15

戻りコード（整数型）
KOBE HPC Spring School 201913

Windowオブジェクトの開放【C】int MPI_Win_free( MPI_Win *win )【F】mpi_win_free( win, ierr )生成したWindowオブジェクトを開放する．

[Input/Output]
win:
生成したwindowオブジェクト[Output]ierr:

2019/3/15

戻りコード（整数型）

KOBE HPC Spring School 201914

リモートプロセスのデータの獲得【C】int MPI_Get( void *oaddr, int ocount, MPI_Datatype odatatype,int target_rank, MPI_Aint tdisp, int tcount, MPI_Datatypetdatatype, MPI_Win win)
【F】mpi_get( oaddr, ocount, odatetype, target_rank, tdisp, tcount, tdatatype,win, ierr )[Input/Output]
oaddr:
自分のプロセスの，データを格納する変数の先頭アドレス
ocount:
データの個数（整数型）
odatatype:
データの型，MPI_INTEGER，MPI_REALなど．CではMPI_INTなど．
target_rank: リモートプロセスのMPIランク番号
tdisp:
先頭からのずれ（整数型）
獲得する変数の先頭アドレスは，base ＋ tdisp×disp_unit
Fortranの場合，integer(kind=MPI_ADDRESS_KIND):: tdisp と宣言する．
tcount:ターゲット側のデータの個数（整数型）
tdatatype:
ターゲット側のデータの型
FortranではMPI_INTEGER，MPI_REALなど．CではMPI_INTなど
win:
通信するwindowオブジェクト[Output]
ierr:
戻りコード（整数型）
2019/3/15

KOBE HPC Spring School 201915

リモートプロセスへのデータの書込み【C】int MPI_Put( const void *oaddr, int ocount, MPI_Datatype odatatype,
int target_rank, MPI_Aint tdisp, int tcount, MPI_Datatypetdatatype, MPI_Win win)
【F】mpi_put( oaddr, ocount, odatetype, target_rank, tdisp, tcount, tdatatype,win, ierr )[Input/Output]
oaddr:
自分のプロセスの，書き込むデータの先頭アドレス
ocount:
データの個数（整数型）
odatatype:
データの型，MPI_INTEGER，MPI_REALなど．CではMPI_INTなど．
target_rank: リモートプロセスのMPIランク番号
tdisp:
先頭からのずれ（整数型）
獲得する変数の先頭アドレスは，base ＋ tdisp×disp_unit
Fortranの場合，integer(kind=MPI_ADDRESS_KIND):: tdisp と宣言する．
tcount:ターゲット側のデータの個数（整数型）
tdatatype:
ターゲット側のデータの型（整数型）
MPI_INTEGER，MPI_REALなど．CではMPI_INTなど．
win:
通信するwindowオブジェクト[Output]
ierr:
戻りコード（整数型）
2019/3/15

KOBE HPC Spring School 201916

片方向通信関数の引数の意味
リモートのプロセス
target_rank

自分のプロセス
Originbaseoaddress

tdsip*disp_unit

ocountGet
Put

2019/3/15

KOBE HPC Spring School 2019tcount17

片方向通信の同期【C】int MPI_Win_fence( int assert, MPI_Win win )【F】mpi_win_fence( assert, win, ierr )winオブジェクトの片方向通信関数の同期を取る．fence関数の前に片方向通信が終了している．
[Input/Output]
assert:Windowの状態の確認用．
通常は 0でよい．MPI_MODE_NOPRECEDEなど
win:

windowオブジェクト[Output]
ierr:

戻りコード（整数型）

2019/3/15

KOBE HPC Spring School 201918

問題：次のプログラムを作れ
2つのMPIプロセスにおいて，
step 1: 長さ 2nの配列を用意する．どんな型（整数型や実数型）でも良い．
step 2: 最初の状態として，プロセス0では配列に1を代入．プロセス1では配列に2を代入．
step 3: プロセス0の配列の前半部分（長さn）をプロセス1の配列の後半（長さn）にコピーし，プロセス1の配列の前半部分（長さn）をプロセス0の配列の後半（長さn）にコピーする．

この作業について，send-recvの組合せと，put-getの組合せの2つのプログラムを作り，結果を確認する．
n=10くらいでやると，出力をすることにより動作を確認できる．

2019/3/15

KOBE HPC Spring School 201919

プログラムの処理イメージ
最初の状態

2019/3/15

交換後の状態プロセス 0プロセス 1プロセス 0プロセス 1

12

12

12

12

12

12

12

12

12

12

12

12

12

12

12

1212

12

12

12

122

1122

11221122

1122

1122

1122112211221

1

2

2

1

KOBE HPC Spring School 201920

プログラム・スケルトン
send-recv

put-get

# ヘッダ

# ヘッダ

# mpiの初期化# mpiの初期化# 配列を準備# 配列を準備# put, get用のWindowをセット# プロセス0側だけから操作
if( myrank == 0 ) thenif( myrank == 0 ) thenrank = 0 の処理（sendして，recvする）

プロセス0の配列の前半部分を，プロセス1の配列の後半部分に書き込む

else
rank = 1 の処理（recvして，sendする）
endif

プロセス1の配列の前半部分をもらい，プロセス0の配列の後半部分に書き込む
endif# put, getの同期待ち．MPI_Win_fence# 結果の確認# 結果の確認# MPIの終了# MPIの終了

2019/3/15

KOBE HPC Spring School 201921参考：send関数, receive関数

2019/3/15

KOBE HPC Spring School 201922

【復習】 1対1通信 – 送信関数（送り出し側）【Ｃ】
int MPI_Send(void *buff, int count, MPI_Datatype datatype, int dest,
int tag, MPI_Comm comm)※ ランク番号destのプロセスに，変数buffの値を送信する．

buff:

送信するデータの変数名（先頭アドレス）

count:

送信するデータの数（整数型）

datatype: 送信するデータの型
MPI_CHAR, MPI_INT, MPI_DOUBLEなど
dest:

送信先プロセスのランク番号tag:

メッセージ識別番号．送るデータを区別するための番号

comm:

コミュニケータ（例えば，MPI_COMM_WORLD）

関数の戻りコードは，エラーコード

2019/3/15

KOBE HPC Spring School 201923

【復習】 1対1通信 – 送信関数（送り出し側） 【Fortran】
mpi_send( buff, count, datatype, dest, tag, comm, ierr )

buff:

送信するデータの変数名（先頭アドレス）

count:

送信するデータの数（整数型）

datatype: 送信するデータの型
MPI_INTEGER, MPI_DOUBLE_PRECISION, MPI_CHARACTER など
dest:

送信先のプロセス番号tag:

メッセージ識別番号．送るデータを区別するための番号

comm:

コミュニケータ（例えば，MPI_COMM_WORLD）

ierr:

戻りコード（整数型）

2019/3/15

KOBE HPC Spring School 201924

【復習】 1対1通信 – 受信関数（受け取り側）【C】
int MPI_Recv(void *buff, int count, MPI_Datatype datatype, int source,
int tag, MPI_Comm comm, MPI_Status *status)※ ランク番号sourceのプロセスから送られたデータを，変数buffに格納する．

buff:

受信するデータのための変数名（先頭アドレス）

count:

受信するデータの数（整数型）

datatype: 受信するデータの型
MPI_CHAR, MPI_INT, MPI_DOUBLEなど
source:

送信してくる相手プロセスのランク番号tag:

メッセージ識別番号．送られて来たデータを区別するための番号

comm:コミュニケータ（例えば，MPI_COMM_WORLD）

status:

通信の状態を格納するオブジェクト．MPI_Sendにはないので注意．

関数の戻りコードは，エラーコード

2019/3/15

KOBE HPC Spring School 201925

【復習】 1対1通信 – 受信関数（受け取り側） 【Fortran】
mpi_recv( buff, count, datatype, source, tag, comm, status, ierr )

buff:

受信するデータのための変数名（先頭アドレス）

count:

受信するデータの数（整数型）

datatype: 受信するデータの型
MPI_INTEGER, MPI_DOUBLE_PRECISION, MPI_CHARACTER など
source:

送信してくる相手のプロセス番号tag:

メッセージ識別番号．送られて来たデータを区別するための番号

comm:

コミュニケータ（例えば，MPI_COMM_WORLD）

status:

受信の状態を格納するサイズMPI_STATUS_SIZEの配列（整数型）

ierr:

戻りコード（整数型）

2019/3/15

KOBE HPC Spring School 201926