並列プログラミング技法( 詳説)...参考図書等 ・using mpi, third edition w....
TRANSCRIPT
-
並列プログラミング技法(MPI詳説)
高度情報科学技術研究機構 神戸センター 宮内 敦
0
-
本日の講義内容
⚫MPIの基礎知識
⚫MPI関数各論
⚫並列性能評価
⚫具体的な問題への応用
1
-
⚫MPIの基礎知識
1. MPI規格 (MPI standard)
2. MPIライブラリ
2
-
•プロセス並列を前提としたプロセス間のメッセージ交換に関する標準仕様
•適用対象は分散記憶アーキテクチャー上の分散アドレス空間におけるSPMDモデル
•ネットワーク接続された異機種クラスター環境にも対応
•先行したPVM(Parallel Virtual Machine)に不足し
ていた機能を採り入れている
•現在ではスレッド並列・直接メモリアクセス・MPMDの機能も提供されている
◼MPI 規格(MPI standard)とは
3
-
◼規格の内容 (Version 3.1)
・要素通信 (Point-to-Point Communication)
・データ型 (Datatypes)
・集団通信 (Collective Communication)
・コミュニケータ (Groups, Contexts, Communicators and Cashing )
・トポロジ (Process Topologies)
・環境管理 (MPI Environmental Management)
・情報オブジェクト (The Info Object)
・プロセス生成 (Process Creation and Management)
・片側通信 (One-Sided Communications)
・外部インターフェイス (External Interfaces)
・ファイル入出力 (I/O)
・ツール情報 (Tool Support)4
-
◼注意すべき用語・バッファ(buffer)
MPIでは3種類のバッファが使われる送受信バッファ(send/recv buffer)はデータを格納した変数・配列システムバッファ(system buffer)はユーザに不可視(opaque)だが
デッドロックに関係する場合があり要注意添付バッファ(attached buffer)はユーザ自身で割当てる
要素通信のバッファモードにおいてのみ使われる
・復帰(return)と完了(completion)復帰は後続する命令を実行可能な状態
(注:returnを値を返すという意味で用いる場合もある)
完了は全ての手順が終了し通信前に戻った状態
・閉塞/非閉塞(blocking/nonblocking)閉塞通信は送受信バッファが解放されるまで復帰しない非閉塞通信は即座に復帰し、後で完了したか確認する
(これを遅延同期(defer synchronization)という)
・非同期通信(asynchronous communication)非閉塞通信とは違う概念、MPIでは使わない*
*The term asynchronous communication is not used in MPI, pp. 279, Using MPI, 2nd ed., MIT Press, 19995
-
◼参考図書等
・Using MPI, Third EditionW. Gropp, E. Lusk and A. Skjellum, MIT press, 2014
・Using Advanced MPIW. Gropp, T. Hoefler, R. Thakur and E. Lusk, MIT press, 2014
・Parallel Programming in C with MPI and OpenMPM. J. Quinn, McGraw-Hill, 2008
・Parallel Programming with MPIP. Pacheco, Morgan Kaufmann, 1996
・Tutorials*http://www.mcs.anl.gov/research/projects/mpi/tutorial/
*query words = MPI, parallel computing, high performance computing, message passing, etc.
6
-
⚫MPIの基礎知識
1. MPI規格(MPI Standard)
2. MPIライブラリ
7
-
◼特徴・MPI Standardに準拠して開発される・プロセス並列モデル・プロセス間通信でメッセージ交換・オープンソフトから商用ソフトまで多数・ライブラリにより性能の違いやバグ
◼代表的ライブラリ・OpenMPI FT, LA, LAM joint team ☆現在の最大勢力・MPICH* Argonne National Lab. ☆根強い人気・MVAPICH Ohio State Univ. ☆GPUに強み・LAM/MPI Indiana Univ. ★かつてクラスタ向けに人気・CHIMP Edinburgh P.C.C. ★初期のライブラリ・Cray-MPI, IBM-MPI, Intel-MPI, Fujitsu-MPI etc.
*MPICH is pronounced “Em Pee Eye See Aych,” not “Emm Pitch.”, pp. 329, Using MPI, 2nd ed., MIT Press, 1999
8
-
◼実装状況(2016年6月)
Implementation Status(MPI3.1), as of June 2016 (http://mpi-forum.org/)c.f. NBC = NonBlocking Collective, RMA = Remote Memory Access, F08 = Fortran2008
9
-
◼命名規則(Naming conventions)他
⚫ 全ての名前は接頭辞 MPI_ で始まる
⚫ 定数・データ型・状態・演算等は全て大文字
例: MPI_COMM_WORLD, MPI_INT, MPI_SUCCESS, MPI_SUM, etc.
⚫ 関数は最初の一文字のみ大文字、残りは全て小文字
例: MPI_Comm_rank(), MPI_Init(),
MPI_Send(), MPI_Bcast(), etc.
⚫ 通信が成功した時の戻り値は MPI_SUCCESSif(MPI_Xxx())は期待通りに動作しない場合あり
失敗したときの値も実装依存10
-
⚫MPI関数各論
1. 集団通信
2. 要素通信
3. 片側通信
4. マルチスレッド
5. 派生データ型
6. ファイルI/O
7. コミュニケータ・トポロジー
8. プロセス生成
9. その他
11
-
◼集団通信とは
⚫ コミュニケータ内の多プロセス間で一斉に行う通信⚫ 内部で要素通信を呼ぶことで実装されている
◼注意点
⚫ コミュニケータ内の全プロセスで関数呼出しが必要
⚫ 実際に全てのプロセスが送受信するとは限らない
⚫ 引数の値は基本的に全てのプロセスで同じ
⚫ root, comm は全てのプロセスで同じ値を指定する
⚫ 送受信バッファはプロセス毎に異なっても良い
⚫ 関数名にvの付くものはプロセス毎にデータ数が異なる(bcast/scatter/scatterv/gather/gatherv/reduceでは
使用しないバッファに MPI_BOTTOM を指定できる)
12
-
◼集団通信関数一覧
MPI_Bcast
MPI_Scatter
MPI_Scatterv
MPI_Gather
MPI_Gatherv
MPI_Reduce
MPI_Scan
MPI_Exscan
MPI_Allgather
MPI_Allgatherv
MPI_Alltoall
MPI_Alltoallv
MPI_Alltoallw
MPI_Allreduce
MPI_Reduce_scatter_block
MPI_Reduce_scatter
MPI_Barrier
MPI_Ibcast
MPI_Iscatter
MPI_Iscatterv
MPI_Igather
MPI_Igatherv
MPI_Ireduce
MPI_Iscan
MPI_Iexscan
MPI_Iallgather
MPI_Iallgatherv
MPI_Ialltoall
MPI_Ialltoallv
MPI_Ialltoallw
MPI_Iallreduce
MPI_Ireduce_scatter_block
MPI_Ireduce_scatter
MPI_Ibarrier
Blocking Nonblocking
One to All
All to One
All to All
Some to All
✔✔ ✔✔ ✔✔ ✔✔ ✔✔ ✔✔✔✔ ✔✔ ✔✔ ✔✔ ✔✔ ✔✔ ✔✔ ✔✔ ✔
✔
IntercommMPI_IN_PLACE
13
-
◼MPI_Bcast
int MPI_Bcast( void* buffer, int count, MPI_Datatype datatype,int root, MPI_Comm comm )
int buf[2];
MPI_Bcast(buf, 2, MPI_INT, 1, MPI_COMM_WORLD);
Bcast
MPI_COMM_WORLD
P0
P1
P2
P3
buf
0 1
buf
0 1
buf
0 1
buf
0 1
MPI_COMM_WORLD
P0
P1
P2
P3
buf
0 1
buf
0 1
buf
0 1
buf
0 1
Function prototype
Example (bcast.c)
• root の持つデータを全プロセスにコピーする
14
注: 本講習では引数の色は変数の属性を示し 入力、出力、入出力 を表す
-
◼MPI_Scatter
int MPI_Scatter( const void* sendbuf, int sendcount, MPI_Datatype sendtype,void* recvbuf, int recvcount, MPI_Datatype recvtype,
int root, MPI_Comm comm )
int sbuf[8], rbuf[2];
MPI_Scatter(sbuf, 2, MPI_INT, rbuf, 2, MPI_INT, 1, MPI_COMM_WORLD);
Scatter
MPI_COMM_WORLD
sbuf
0 1 2 3 4 5 6 7
P0
P1
P2
P3
sbuf
0 1 2 3 4 5 6 7
sbuf
0 1 2 3 4 5 6 7
sbuf
0 1 2 3 4 5 6 7
MPI_COMM_WORLD
P0
P1
P2
P3
rbuf
0 1
rbuf
0 1
rbuf
0 1
rbuf
0 1
Function prototype
Example (scatter.c)
• root の持つデータを全プロセスに分配する
15
-
◼MPI_Scatterv
int MPI_Scatterv( const void* sendbuf, const int sendcounts[],const int displs[], MPI_Datatype sendtype,
void* recvbuf, int recvcount, MPI_Datatype recvtype,int root, MPI_Comm comm )
int sbuf[11], rbuf[3];
int scounts[4]={1,2,3,2}, displs[4]={0,2,5,9};MPI_Scatterv(sbuf, scounts, displs, MPI_INT,
rbuf, 3, MPI_INT, 1, MPI_COMM_WORLD);
Scatterv
MPI_COMM_WORLD
P0
P1
P2
P3
sbuf
0 1 2 3 4 5 6 7 8 9 10
sbuf
0 1 2 3 4 5 6 7 8 9 10
sbuf
0 1 2 3 4 5 6 7 8 9 10
sbuf
0 1 2 3 4 5 6 7 8 9 10
MPI_COMM_WORLD
P0
P1
P2
P3
rbuf
0 1 2
rbuf
0 1 2
rbuf
0 1 2
rbuf
0 1 2
Function prototype
Example (scatterv.c)
16
-
⚫MPI_Scatter/MPI_Scatterv の注意点
MPI_Scatterv は MPI_Scatter をデータ長可変にしたもの
送信側パラメータ (sendbuf, sendcount, sendcounts[], displs[], sendtype)
• root の値のみ有効、それ以外の値は無視される
受信側パラメータ (recvbuf, recvcount, recvtype)
• recvcount は受信するデータ長以上であれば良い• recvcount が受信するデータ長未満の場合はエラー• Scatterv の recvcount は本来プロセス毎に異なる
通常は recvcount=sendcounts[rank] とする(前頁は全て3でも正常動作)
• recvbuf の配列長は recvcount 以上必要• recvbuf の不要な配列要素は無視される• recvtype と sendtype は異なっても良いが
受信データ長が送信より少ないとエラー
分割されたデータの送信先はプロセス番号順Scattervではdisplsの値によって非連続的な順番も可能但しデータに重なりあるとエラーになるので注意
17
-
◼MPI_Gather
int MPI_Gather( const void* sendbuf, int sendcount, MPI_Datatype sendtype,void* recvbuf, int recvcount, MPI_Datatype recvtype,
int root, MPI_Comm comm )
int sbuf[2], rbuf[8];
MPI_Gather(sbuf, 2, MPI_INT, rbuf, 2, MPI_INT, 1, MPI_COMM_WORLD);
Gather
MPI_COMM_WORLD
P0
P1
P2
P3
sbuf
0 1
sbuf
0 1
sbuf
0 1
sbuf
0 1
MPI_COMM_WORLD
rbuf
0 1 2 3 4 5 6 7
P0
P1
P2
P3
rbuf
0 1 2 3 4 5 6 7
rbuf
0 1 2 3 4 5 6 7
rbuf
0 1 2 3 4 5 6 7
Function prototype
Example (gather.c)
• 全プロセスのデータを root に収集する
18
-
◼MPI_Gatherv
int MPI_Gatherv( const void* sendbuf, int sendcount, MPI_Datatype sendtype,void* recvbuf, const int recvcounts[],
const int displs[], MPI_Datatype recvtype,int root, MPI_Comm comm )
int sbuf[3], rbuf[11];
int rcounts[4]={1,2,3,2}, displs[4]={0,2,5,9};MPI_Gatherv(sbuf, 3, MPI_INT,
rbuf, rcounts, displs, MPI_INT, 1, MPI_COMM_WORLD);
Gatherv
MPI_COMM_WORLD
P0
P1
P2
P3
rbuf
0 1 2 3 4 5 6 7 8 9 10
rbuf
0 1 2 3 4 5 6 7 8 9 10
rbuf
0 1 2 3 4 5 6 7 8 9 10
rbuf
0 1 2 3 4 5 6 7 8 9 10
MPI_COMM_WORLD
P0
P1
P2
P3
sbuf
0 1 2
sbuf
0 1 2
sbuf
0 1 2
sbuf
0 1 2
Function prototype
Example (gatherv.c)
19
-
⚫MPI_Gather/MPI_Gathervの注意点
MPI_Gatherv は MPI_Gather をデータ長可変にしたもの
送信側パラメータ(sendbuf, sendcount, sendtype)
• Gatherv の sendcount はプロセス毎に異なる通常は recvcounts[rank] とする
受信側パラメータ (recvbuf, recvcount, recvcounts[], displs[], recvtype)
• root の値のみ有効、それ以外の値は無視される• recvcount, recvcounts[] は受信するデータ長以上であれば良い• recvcount, recvcounts[] が受信するデータ長未満の場合はエラー• recvcounts[] の各要素の値は本来異なる
gatherv.cの場合 recvcounts[]={1,2,3,2}でも{3,3,3,3}でも正常に動作
• recvbuf の配列長は (recvcount or recvcounts[])*nproc以上必要• recvbuf の不要な配列要素は無視される• recvtype と sendtype は異なっても良いが
受信データのバイト数が不足するとエラー
受信データの開始位置はプロセス番号順Gathervではdisplsの値によって非連続的な順番も可能但しデータに重なりあるとエラーになるので注意
20
-
◼MPI_Reduce
int MPI_Reduce( const void* sendbuf, void* recvbuf, int count, MPI_Datatype datatype,MPI_Op op, int root, MPI_Comm comm )
int sbuf[2], rbuf[2];
MPI_Reduce(sbuf, rbuf, 2, MPI_INT, MPI_SUM, 1, MPI_COMM_WORLD);
Reduce
MPI_COMM_WORLD
P0
P1
P2
P3
sbuf
0 1
2 5
sbuf
0 1
0 7
sbuf
0 1
6 3
sbuf
0 1
4 1
MPI_COMM_WORLD
P0
P1
P2
P3
rbuf
0 1
rbuf
0 1
12 16
rbuf
0 1
rbuf
0 1
12=2+0+6+4
16=5+7+3+1
Function prototype
Example (reduce.c)
• 全プロセスのデータを root に収集して総和
21
-
◼MPI_Scan
int MPI_Scan( const void* sendbuf, void* recvbuf, int count, MPI_Datatype datatype,MPI_Op op, MPI_Comm comm )
int sbuf[2], rbuf[2];
MPI_Scan(sbuf, rbuf, 2, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
• 自分を含め自分より番号の若いプロセス上の値の総和
Scan
MPI_COMM_WORLD
P0
P1
P2
P3
sbuf
0 1
2 5
sbuf
0 1
0 7
sbuf
0 1
6 3
sbuf
0 1
4 1
MPI_COMM_WORLD
P0
P1
P2
P3
rbuf
0 1
2 5
rbuf
0 1
2 12
rbuf
0 1
8 15
rbuf
0 1
12 16
2=2+0
12=5+7
8=2+0+6
15=5+7+3
12=2+0+6+4
16=5+7+3+1
2=2
5=5
Function prototype
Example (scan.c)
22
-
◼MPI_Exscan
int MPI_Exscan( const void* sendbuf, void* recvbuf, int count, MPI_Datatype datatype,MPI_Op op, MPI_Comm comm )
int sbuf[2], rbuf[2];
MPI_Exscan(sbuf, rbuf, 2, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
• 自分を除く自分より番号の若いプロセス上の値の総和
Exscan
MPI_COMM_WORLD
P0
P1
P2
P3
sbuf
0 1
2 5
sbuf
0 1
0 7
sbuf
0 1
6 3
sbuf
0 1
4 1
MPI_COMM_WORLD
P0
P1
P2
P3
rbuf
0 1
-- --
rbuf
0 1
2 5
rbuf
0 1
2 12
rbuf
0 1
8 15
2=2
5=5
2=2+0
12=5+7
8=2+0+6
15=5+7+3
--=--
--=--
Function prototype
Example (exscan.c)
23
-
⚫MPI_Reduce/MPI_Scan/MPI_Exscanの注意点
受信バッファ(recvbuf) の値
• MPI_Reduce では root のみ値が返される• MPI_Scan では全プロセスで値が返される• MPI_Exscan ではプロセス0以外で値が返される• 値の返されない recvbuf の内容は通信の前後で変わらない
24
-
int in, out;in = 10; // local array sizeout = 0;MPI_Exscan( &in, &out, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD );printf(“rank %d begins from %d¥n”, rank, out);
⚫MPI_Scan/MPI_Exscanの代表的な使い方
大域配列のオフセット値を求める
Process0 Process1 Process2 Process3
0 9 0 9 0 9 0 9
0 9 10 19 20 29 30 39
分散配列
大域配列
>mpiexec –n 4 ./a.outrank 0 begins from 0rank 1 begins from 10rank 2 begins from 20rank 3 begins from 30>
25
-
MPI_MAX
MPI_MIN
MPI_MAXLOC
MPI_MINLOC
MPI_SUM
MPI_PROD
MPI_LAND
MPI_LOR
MPI_LXOR
MPI_BAND
MPI_BOR
MPI_BXOR
⚫定義済みの主なデータ型と演算
Integer: MPI_INT, MPI_LONG, MPI_UNSIGNED, etc.
Floating point: MPI_FLOAT, MPI_DOUBLE, etc.
Complex: MPI_C_COMPLEX, MPI_C_DOUBLE_COMPLEX, etc.
Logical: MPI_C_BOOL, etc.
Byte: MPI_BYTE
Integer Floating Complex Logical Byte
✔ ✔ ✔
✔ ✔
✔ ✔
✔ ✔
最大値
最小値
最大値と指標
最小値と指標
総和
総積
論理積
論理和
論理排他和
ビット積
ビット和
ビット排他和
26
-
struct{ float value;int index;
} in[2], out[2]; in[0].value = ...; in[0].index = ...;in[1].value = ...; in[1].index = ...;MPI_Reduce( in, out, 2, MPI_FLOAT_INT, ¥
MPI_MAXLOC, 1, MPI_COMM_WORLD );
⚫MPI_MAXLOC/MPI_MINLOCの使い方
in[0].value in[0].index in[1].value in[1].indexProcess0 1.2 3 4.2 9Process1 3.5 4 1.3 2Process2 2.3 1 8.4 7Process3 4.8 0 5.7 3
out[0].value out[0].index out[1].value out[1].indexProcess1 4.8 0 8.4 7
例
プロセス1には以下の値が格納される
最大の実数値とその指標を探す
• 指標は必ず整数• valueが整数の場合はMPI_2INT型(MPI_INT_INTではない)
Example (maxloc.c)
27
-
⚫ユーザ定義演算
typedef struct{ float vx, vy; } fvec2;
void addvec(void *invec, void* iovec, int *len, MPI_Datatype *dptr){
fvec2 *a = (fvec2 *)invec; // in vectorfvec2 *b = (fvec2 *)iovec; // inout vectorfor(int i=0; ivx += a->vx; b->vy += a->vy;++a; ++b;
}}
MPI_Datatype vtype;MPI_Type_contiguous(2, MPI_FLOAT, &vtype);MPI_Type_commit(&vtype);
MPI_Op myOp;MPI_Op_create(addvec, 1, &myOp);
fvec2 in[4], sum[4];MPI_Reduce(in, sum, 4, vtype, myOp, 1, MPI_COMM_WORLD);
MPI_Op_free(&myOp);
2次元ベクトル合成
戻り値なし
可換演算は true (実装依存)を指定
fvec2をMPI_Datatypeに変換
演算本体(結果を iovec 側に格納する)
Example (reduceop.c)
dptrは省略不可
この例では4が代入される
28
-
◼MPI_Allgather
int MPI_Allgather( const void* sendbuf, int sendcount, MPI_Datatype sendtype,void* recvbuf, int recvcount, MPI_Datatype recvtype,MPI_Comm comm )
int sbuf[2], rbuf[8];
MPI_Allgather(sbuf, 2, MPI_INT, rbuf, 2, MPI_INT, MPI_COMM_WORLD);
Allgather
MPI_COMM_WORLD
P0
P1
P2
P3
sbuf
0 1
sbuf
0 1
sbuf
0 1
sbuf
0 1
MPI_COMM_WORLD
rbuf
0 1 2 3 4 5 6 7
P0
P1
P2
P3
rbuf
0 1 2 3 4 5 6 7
rbuf
0 1 2 3 4 5 6 7
rbuf
0 1 2 3 4 5 6 7
Function prototype
Example (allgather.c)
• 引数はGatherとほぼ同じ(rootが無い)、動作はGather+Bcast
29
-
◼MPI_Allgatherv
int MPI_Allgatherv( const void* sendbuf, int sendcount, MPI_Datatype sendtype,void* recvbuf, const int recvcounts[], const int displs[], MPI_Datatype recvtype, MPI_Comm comm )
int sbuf[3], rbuf[11];
int rcounts[4]={1,2,3,2}, displs[4]={0,2,5,9};MPI_Allgatherv(sbuf, 3, MPI_INT,
rbuf, rcounts, displs, MPI_INT, MPI_COMM_WORLD); 注:rcounts, displsはrootの値だけ有効、sendcountの値はプロセス毎に異なる(この例ではプロセス2)
・引数はGathervとほぼ同じ(rootが無い)、動作はGatherv+Bcast
Allgatherv
MPI_COMM_WORLD
P0
P1
P2
P3
rbuf
0 1 2 3 4 5 6 7 8 9 10
rbuf
0 1 2 3 4 5 6 7 8 9 10
rbuf
0 1 2 3 4 5 6 7 8 9 10
rbuf
0 1 2 3 4 5 6 7 8 9 10
MPI_COMM_WORLD
P0
P1
P2
P3
sbuf
0 1 2
sbuf
0 1 2
sbuf
0 1 2
sbuf
0 1 2
Function prototype
Example (allgatherv.c)
30
-
◼MPI_Allreduce
int MPI_Allreduce( const void* sendbuf, void* recvbuf, int count, MPI_Datatype datatype,MPI_Op op, MPI_Comm comm )
int sbuf[2], rbuf[2];
MPI_Allreduce(sbuf, rbuf, 2, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
• 引数はReduceとほぼ同じ(rootが無い)• 動作はReduce+Bcast
Allreduce
MPI_COMM_WORLD
P0
P1
P2
P3
sbuf
0 1
2 5
sbuf
0 1
0 7
sbuf
0 1
6 3
sbuf
0 1
4 1
MPI_COMM_WORLD
P0
P1
P2
P3
rbuf
0 1
12 16
rbuf
0 1
12 16
rbuf
0 1
12 16
rbuf
0 1
12 16
12=2+0+6+4
16=5+7+3+1
12=2+0+6+4
16=5+7+3+1
12=2+0+6+4
16=5+7+3+1
12=2+0+6+4
16=5+7+3+1
Function prototype
Example (allreduce.c)
31
-
◼MPI_Reduce_scatter_block
int MPI_Reduce_scatter_block( const void* sendbuf, void* recvbuf, int recvcount, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm )
int sbuf[8], rbuf[2];
MPI_Scatter_block(sbuf, rbuf, 2, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
• 引数はReduceとほぼ同じ(rootが無い)• 動作はReduce+Scatter(sumにReduceしてScatter)
Reduce_scatter
_block
MPI_COMM_WORLD
sbuf 0 4 25 1 63 7
0 1 2 3 4 5 6 7
P0
P1
P2
P3
sbuf 2 3 01 4 65 7
0 1 2 3 4 5 6 7
sbuf 5 7 62 3 01 4
0 1 2 3 4 5 6 7
sbuf 7 0 14 5 36 2
0 1 2 3 4 5 6 7
MPI_COMM_WORLD
P0
P1
P2
P3
rbuf
0 1
14 15
rbuf
0 1
12 14
rbuf
0 1
20 9
rbuf
0 1
13 15
sum
14 15 1214 20 9 13 15
14=2+0+5+7
15=5+3+1+6
12=1+5+2+4
14=3+4+7+0
20=7+7+4+2
9=0+2+6+1
13=4+1+3+5
15=6+6+0+3
Function prototype
Example (reducescatterblock.c)
32
-
◼MPI_Reduce_scatter
int MPI_Reduce_scatter( const void* sendbuf, void* recvbuf, const int recvcounts[], MPI_Datatype datatype, MPI_Op op, MPI_Comm comm )
int sbuf[8], rbuf[3];
int rcounts[4]={1,2,3,2};MPI_Scatter(sbuf, rbuf, rcounts, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
• Reduceとほぼ同じ(引数にrootが無い、recvcountが配列)• 動作はReduce+Scatterv(sumにReduceしてScatterv)
Reduce_scatter
MPI_COMM_WORLD
sbuf 0 4 25 1 63 7
0 1 2 3 4 5 6 7
P0
P1
P2
P3
sbuf 2 3 01 4 65 7
0 1 2 3 4 5 6 7
sbuf 5 7 62 3 01 4
0 1 2 3 4 5 6 7
sbuf 7 0 14 5 36 2
0 1 2 3 4 5 6 7
sum
14 15 1214 20 9 13 15
14=2+0+5+7
15=5+3+1+6
12=1+5+2+4
14=3+4+7+0
20=7+7+4+2
9=0+2+6+1
13=4+1+3+5
15=6+6+0+3
MPI_COMM_WORLD
P0
P1
P2
P3
sbuf
0 1 2
14
sbuf
0 1 2
15 12
9sbuf
0 1 2
14 20
sbuf
0 1 2
13 15
Function prototype
Example (reducescatter.c)
33
-
◼MPI_Alltoall
int MPI_Alltoall( const void* sendbuf, int sendcount, MPI_Datatype sendtype, void* recvbuf, int recvcount, MPI_Datatype recvtype,MPI_Comm comm )
int sbuf[8], rbuf[8];
MPI_Alltoall(sbuf, 2, MPI_INT, rbuf, 2, MPI_INT, MPI_COMM_WORLD);
• 引数はGatherとほぼ同じ(rootが無い)• 動作はプロセス毎にsbufの開始位置を変えてGather, またはプロセス毎にrbufの開始位置を変えてScatter• sbuf, rbufに十分な大きさがあればsendcount!=recvcountも可能(intercommの場合など)
Alltoall
MPI_COMM_WORLD
sbuf
0 1 2 3 4 5 6 7
sbuf
0 1 2 3 4 5 6 7
sbuf
0 1 2 3 4 5 6 7
sbuf
0 1 2 3 4 5 6 7
P0
P1
P2
P3
MPI_COMM_WORLD
rbuf
0 1 2 3 4 5 6 7
rbuf
0 1 2 3 4 5 6 7
rbuf
0 1 2 3 4 5 6 7
rbuf
0 1 2 3 4 5 6 7
P0
P1
P2
P3
Function prototype
Example (alltoall.c)
34
-
◼MPI_Alltoallv
int MPI_Alltoallv( const void* sendbuf, const int sendcounts[], const int sdispls[], MPI_Datatype sendtype,
void* recvbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype,
MPI_Comm comm )
scounts rcountsProcess0 { a0, a1, a2, a3 } { a0, b0, c0, d0 }Process1 { b0, b1, b2, b3 } { a1, b1, c1, d1 }Process2 { c0, c1, c2, c3 } { a2, b2, c2, d2 }Process3 { d0, d1, d2, d3 } { a3, b3, c3, d3 }
• scounts, rcountsの値は互いに転置関係
⚫ scounts, sdispls, rcounts, rdisplsの値は整合的でなければならない
• sendbuf, recvbufには十分な大きさが必要
int sendbuf[ssize]; // ssize > sdispls[0]+sdispls[1]+...
int recvbuf[rsize]; // rsize > rdispls[0]+rdispls[1]+...
• 引数はAlltoallとほぼ同じ(sendcount, recvcountが配列)• 動作は各プロセスがsbufとrbufの開始位置を変えてGather
Function prototype
35
-
int sbuf[13], rbuf[13];
int scounts[4], sdispls[4], rcounts[4], rdispls[4];MPI_Alltoallv(sbuf, scounts, sdispls, MPI_INT,
rbuf, rcounts, rdispls, MPI_INT, 1, MPI_COMM_WORLD);
scounts sdispls rcounts rdisplsProcess0 { 2, 1, 3, 2 } { 0, 2, 4, 8 } { 2, 1, 2, 3 } { 0, 3, 5, 9 }Process1 { 1, 2, 2, 3 } { 1, 3, 5, 7 } { 1, 2, 3, 2 } { 2, 4, 7,11 }Process2 { 2, 3, 1, 2 } { 2, 5, 8,11 } { 3, 2, 1, 2 } { 0, 4, 8,10 }Process3 { 3, 2, 2, 1 } { 2, 6, 9,12 } { 2, 3, 2, 1 } { 1, 3, 6, 9 }
• プロセス数が4で上記のパラメータの場合
Alltoallv
MPI_COMM_WORLD
sbuf
0 1 2 3 4 5 6 7 8 9 10 11 12
sbuf
0 1 2 3 4 5 6 7 8 9 10 11 12
sbuf
0 1 2 3 4 5 6 7 8 9 10 11 12
sbuf
0 1 2 3 4 5 6 7 8 9 10 11 12
P0
P1
P2
P3
MPI_COMM_WORLD
rbuf
0 1 2 3 4 5 6 7 8 9 10 11 12
rbuf
0 1 2 3 4 5 6 7 8 9 10 11 12
rbuf
0 1 2 3 4 5 6 7 8 9 10 11 12
rbuf
0 1 2 3 4 5 6 7 8 9 10 11 12
P0
P1
P2
P3
Example (alltoallv.c)
36
-
◼MPI_Alltoallw
int MPI_Alltoallw( const void* sendbuf, const int sendcounts[], const int sdispls[], const MPI_Datatype sendtypes[],
void* recvbuf, const int recvcounts[], const int rdispls[], const MPI_Datatype recvtypes[],
MPI_Comm comm )
⚫ プロセス毎に異なったDatatype
⚫ sendbuf, recvbufには十分な大きさが必要
⚫ sdispls, rdisplsはバイト数で指定する
• 引数はAlltoallvとほぼ同じ(sendtypes, recvtypesが配列)
sdispls[0] = rdispls[0] = 0;for(int i=0; i
-
◼MPI_Barrier
int MPI_Barrier( MPI_Comm comm )
⚫ 全プロセスがコールするまで待ち合わせる
⚫ 全プロセスが同時に復帰する訳ではない(タイムラグ有り)
⚫ ハードウェアによって高速化している場合もある
MPI_Barrier(MPI_COMM_WORLD);
Function prototype
Example
38
-
int MPI_Reduce_local( const void* inbuf, void* inoutbuf, int count, MPI_Datatype datatype, MPI_Op op )
⚫ 引数にrootとMPI_Commが無い、プロセス内のみでreduce
int ibuf[2], iobuf[2];
MPI_Reduce_local(ibuf, iobuf, 2, MPI_INT, MPI_SUM );
int MPI_Op_commutative (MPI_Op op, int *commute )
⚫ 演算が可換か否かを返す(true or false)
int iscommutative;
MPI_Op_commutative(MPI_INT, &iscommutative );
◼その他の関数Function prototype
Function prototype
Example
Example
39
-
◼非閉塞(Nonblocking)通信
⚫ 通信相手の状態に関係なく即座に復帰
⚫ 後からTest/Wait関数で完了
⚫ ノード内に通信用コプロセッサ等を持つ場合にはLinuxカーネルを改造し通信処理をオフロード(off-load)することで通信の隠蔽(overwrap)が可能な場合あり*
⚫ オフロードできない場合はTest/Wait関数の中で通信を開始するので通信は隠蔽できない(multi-thread化すれば隠蔽可能)
⚫ 非閉塞通信は本来デッドロックを避けるためMPI standardの中に隠蔽に関する規定は無い
*With suitable hardware, transfer … may proceed concurrently with computations ... , p.47, line 20, MPI Standard 3.1
*whether such overlapping is possible may depend on the hardware …, p.108, line 11, Using MPI, 3rd ed., MIT Press, 2014
40
-
int buf[3] = ... , sbuf[2] = ... , rbuf[2] = ... ; // buffersMPI_Request req[2];
MPI_Ibcast( buf, 3, MPI_INT, MPI_COMM_WORLD, &req[0] );
// some workloads
MPI_Ireduce( sbuf, rbuf, 2, MPI_INT, MPI_SUM,1, MPI_COMM_WORLD, &req[1] );
// another workloads
MPI_Waitall(2, req, MPI_STATUSES_IGNORE);
⚫非閉塞通信の使い方
IbcastとIreduceを使う場合
即座に復帰
待ち合せて完了
⚫ 閉塞通信より引数が一つ(&req[ ])増えただけ
⚫ Waitall が復帰するまで buf, sbuf, rbuf にアクセスできない
⚫ その他の集団通信の使い方も上記と同じ
⚫ Wait, Testについては要素通信の中で説明する
即座に復帰
集団通信でstatusは不要
Example
41
-
42
A busy wait B
A B
MPI_Barrier
MPI_Barrier
Process 0
Process 1
time
仮定:処理AとBは同時に実行できない
➡ Bは同時に開始するが busy wait が発生する
➡ busy wait は解消するが Bは同時に開始しない
A B
A C
MPI_Ibarrier
MPI_Ibarrier
Process 0
Process 1
time
C
MPI_Wait
B
MPI_Wait
⚫非閉塞Barrierの使い方
1. blocking-barrierで分離する (Bulk Synchronous)
2. A,Bと同時実行可能なCを挟んでnon-blocking barrierで分離する
-
◼送受信バッファの共通化
⚫ 送信バッファにMPI_IN_PLACEを指定するもの◼ rootのみ
MPI_Gather, MPI_Gatherv, MPI_Reduce◼ 全プロセスで指定必要
MPI_Scan, MPI_Exscan, MPI_Allgather, MPI_Allgatherv, MPI_Allreduce, MPI_Alltoall, MPI_Alltoallv, MPI_Alltoallw, MPI_Reduce_scatter_block, MPI_Reduce_scatter
⚫ 受信バッファにMPI_IN_PLACEを指定するもの◼ rootのみ
MPI_Scatter, MPI_Scatterv⚫ 非対応
MPI_Bcast , MPI_Barrier,MPI_Reduce_local, MPI_Op_commutative
✓ 非閉塞通信も閉塞通信と全く同じ✓ Gather, Gathervではrootのデータは既にrecvbufに入っていると仮定される
43
-
⚫MPI関数各論
1. 集団通信
2. 要素通信
3. 片側通信
4. マルチスレッド
5. 派生データ型
6. ファイルI/O
7. コミュニケータ・トポロジー
8. プロセス生成
9. その他
44
-
◼要素通信とは
⚫ 最も基本的な通信方法⚫ コミュニケータ内の一対のプロセス間の通信⚫ 一方が送信し、他方が受信する⚫ 送信側が制御する push mechanism⚫ 送受信を一つにまとめた複合通信が用意されている⚫ オーバーヘッドが軽い持続通信が用意されている
◼注意点
⚫ 送信側にはモードがある⚫ 不適切なモード選択はデッドロックを引き起こす
45
-
◼要素通信関数一覧
MPI_Send
MPI_Ssend
MPI_Bsend
MPI_Rsend
MPI_Recv
MPI_Mrecv*
MPI_Probe
MPI_Mprobe*
MPI_Sendrecv
MPI_Sendrecv_replace
MPI_Send_init
MPI_Ssend_init
MPI_Bsend_init
MPI_Rsend_init
MPI_Recv_init
MPI_Isend
MPI_Issend
MPI_Ibsend
MPI_Irsend
MPI_Irecv
MPI_Imrecv*
MPI_Iprobe
MPI_Improbe*
MPI_Start
MPI_Startall
Blocking Nonblocking
Send
Recv
Probe
Intercomm
✔✔✔✔
✔✔
✔✔
✔✔✔✔✔
Persistent
Combined
46* MPI_Mrecv, MPI_Mprobe, MPI_Imrecv, MPI_Improbeはマルチスレッドで説明する
-
◼要素通信関数一覧(続)
MPI_Wait
MPI_Waitany
MPI_Waitall
MPI_Waitsome
Buffer
Complete
MPI_Test
MPI_Testany
MPI_Testall
MPI_Testsome
Request
Status
MPI_Cancel
MPI_Request_free
MPI_Request_get_status
MPI_Test_cancelled
MPI_Get_count
MPI_Get_element*
MPI_Get_element_x*
MPI_Buffer_attach
MPI_Buffer_detach
47
Blocking Nonblocking
* MPI_Get_element, MPI_Get_element_xの説明は省略
-
◼MPI_Send
int MPI_Send( const void* buf, int count, MPI_Datatype datatype,int dest, int tag, MPI_Comm comm )
int buf[2];
MPI_Send(buf, 2, MPI_INT, 1, 9, MPI_COMM_WORLD);
◼MPI_Recv
int MPI_Recv( void* buf, int count, MPI_Datatype datatype,int source, int tag, MPI_Comm comm, MPI_Status *status )
int buf[2];
MPI_Status status;MPI_Recv(buf, 2, MPI_INT, 0, 9, MPI_COMM_WORLD, &status );
⚫ bufから始まる2つの整数にタグ9を付けてプロセス1に送る
⚫ タグ9が付いた2つの整数をプロセス0から受け取りbufに格納する
タグは通信の順序を識別するためにユーザが付ける整数 statusはMPI_SOURCE, MPI_TAG, MPI_ERRORからなる構造体 MPI_Sendのbuf以外の引数をエンベロープ(envelope)と呼ぶ
48
Function prototype
Function prototype
-
int sbuf[2], rbuf[2];
if(rank==0) {sbuf[0] = 10; sbuf[1] = 20;MPI_Send( sbuf, 2, MPI_INT, 1, 9, MPI_COMM_WORLD );
} else if(rank==1)MPI_Recv( rbuf, 2, MPI_INT, 0, 9, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
⚫ 2プロセスの例
Proc0からProc1にデータを送る
注:statusは必要ないのでMPI_STATUS_IGNOREで無効にしている
① Proc0がSendを開始② Proc1がRecvを開始③ Proc1のRecvが復帰(完了)④ Proc0のSendが復帰(完了)
Proc0 Proc1
Send Recv
正常終了
① ②
③④
49
Example (sendrecv01.c)
-
int sbuf[2], rbuf[2];
if(rank==0) {sbuf[0] = 10; sbuf[1] = 20;MPI_Send( sbuf, 2, MPI_INT, 1, 8, MPI_COMM_WORLD );MPI_Recv( rbuf, 2, MPI_INT, 1, 9, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
} else if(rank==1) {sbuf[0] = 20; sbuf[1] = 30;MPI_Recv( rbuf, 2, MPI_INT, 0, 8, MPI_COMM_WORLD, MPI_STATUS_IGNORE );MPI_Send( sbuf, 2, MPI_INT, 0, 9, MPI_COMM_WORLD );
}
Proc0とProc1がデータを交換する(その1)
Proc0 Proc1
Send Recv
正常終了
① ②
③
Recv Send
④⑤⑥
⑦ ⑧
① Proc0がSendを開始② Proc1がRecvを開始③ Proc1のRecvが復帰(完了)④ Proc0のSendが復帰(完了)⑤ Proc1がSendを開始⑥ Proc0がRecvを開始⑦ Proc0のRecvが復帰(完了)⑧ Proc1のSendが復帰(完了)
50
Example (sendrecv02.c)
-
int sbuf[2], rbuf[2];
if(rank==0) {sbuf[0] = 10; sbuf[1] = 20;MPI_Recv( rbuf, 2, MPI_INT, 1, 9, MPI_COMM_WORLD, MPI_STATUS_IGNORE );MPI_Send( sbuf, 2, MPI_INT, 1, 8, MPI_COMM_WORLD );
} else if(rank==1) {sbuf[0] = 20; sbuf[1] = 30;MPI_Recv( rbuf, 2, MPI_INT, 0, 8, MPI_COMM_WORLD, MPI_STATUS_IGNORE );MPI_Send( sbuf, 2, MPI_INT, 0, 9, MPI_COMM_WORLD );
}
Proc0とProc1がデータを交換する(その2)
① Proc0がRecvを開始② Proc1がRecvを開始③ 相手側がSendを開始するまで
Recvは復帰できない④ Recvが復帰しないと
Sendを開始できない
Proc0 Proc1
RecvRecv
②①
互いに相手を待ち続ける
51
Example (sendrecv03.c)
-
int sbuf[2], rbuf[2];
if(rank==0) {sbuf[0] = 10; sbuf[1] = 20;MPI_Send( sbuf, 2, MPI_INT, 1, 8, MPI_COMM_WORLD );MPI_Recv( rbuf, 2, MPI_INT, 1, 9, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
} else if(rank==1) {sbuf[0] = 20; sbuf[1] = 30;MPI_Send( sbuf, 2, MPI_INT, 0, 9, MPI_COMM_WORLD );MPI_Recv( rbuf, 2, MPI_INT, 0, 8, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
}
Proc0とProc1がデータを交換する(その3)
① Proc0がSendを開始② Proc1がSendを開始③ Proc0がデータをバッファリング④ Proc1がデータをバッファリング⑤ Proc0のSendが復帰⑥ Proc1のSendが復帰⑦ Proc0がRecvを開始⑧ Proc1がRecvを開始⑨ Proc0のRecvが復帰⑩ Proc1のRecvが復帰正常終了
システムバッファ
Proc0 Proc1
Send Send① ②④
Recv Recv
③
⑥⑤
⑨ ⑩
⑧⑦
データが小さい場合(
-
int sbuf[2], rbuf[2];
if(rank==0) {sbuf[0] = 10; sbuf[1] = 20;MPI_Send( sbuf, 2, MPI_INT, 1, 8, MPI_COMM_WORLD );MPI_Recv( rbuf, 2, MPI_INT, 1, 9, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
} else if(rank==1) {sbuf[0] = 20; sbuf[1] = 30;MPI_Send( sbuf, 2, MPI_INT, 0, 9, MPI_COMM_WORLD );MPI_Recv( rbuf, 2, MPI_INT, 0, 8, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
}
Proc0とProc1がデータを交換する(その4)
① Proc0がSendを開始② Proc1がSendを開始③ 相手側がRecvを開始するまで
Sendは復帰できない④ Sendが復帰しないと
Recvを開始できない
データが大きい場合(>16KB)Proc0 Proc1
SendSend
②①
互いに相手を待ち続ける
53
Example (sendrecv05.c)
-
◼通信モード
⚫送信関数には4種類のモードがある
• Standard mode
MPI_Send, MPI_Isend, MPI_Send_init
• Synchronous mode
MPI_Ssend, MPI_Issend, MPI_Ssend_init
• Ready mode
MPI_Rsend, MPI_Irsend, MPI_Rsend_init
• Buffered mode
MPI_Bsend, MPI_Ibsend, MPI_Bsend_init
MPI_Recv, MPI_Irecv, MPI_Recv_init
⚫受信関数にはモードがない (push mechanism)
54
-
• Standard
➢ データが小さい場合はシステムバッファにバッファリング
➢ データが大きい場合は受信側とハンドシェイク
• Synchronous
➢ バッファリングせず受信側とハンドシェイク
➢ 最もデッドロック起き易い
➢ このモードでデッドロックしないプログラムをsafe programと呼ぶ
• Ready
➢ 受信側の状態に関係なく即座に送信
➢ 受信側が既に待機している場合のみ開始可能
➢ それ以外はエラーまたは不定状態
• Buffered
➢ ユーザが設定した添付バッファにバッファリング
⚫各モードの特徴
55
-
• MPICHでは2つの通信プロトコルを定義*
• Eager protocolエンベロープに続けてデータも送信
• Rendezvous protocolエンベロープだけ送り、準備が整ったらデータを送信
• 送信関数の引数
* MPI standardに規定はない
⚫通信モードの実装方法
56
MPI_Send( buf, 2, MPI_INT, 1, 9, MPI_COMM_WORLD );
data** envelope
message
** buf はデータへのポインタ
-
57
⚫ Eager protocol
a) recv先行
b) send先行
• recv 側の基本動作
◼ 未処理の recv を expected queue に登録◼ 未処理の send を unexpected queue に登録◼ send が到着すると expected queue に matching recv を探す◼ recv が発行されると unexpected queue に matching send を探す
send recv unexpected queue
expected queue
inquire
no matching send
register recvinquire
matching recv exists
rbufstore
send
recv unexpected queue
expected queue
register send
matching send exists
inquire
no matching recv
rbufcopy
sysbufstore
inquire
-
58
⚫ Rendezvous protocol
c) recv先行
d) send先行
send recv unexpected queue
expected queue
inquire
no matching send
register recvinquire
matching recv exists
rbufstore
send
recv unexpected queue
expected queue
inquire
register send
inquire
no matching recv
rbufstore
matching send exists
-
• Standard (短メッセージはバッファリング有り、Eagerそれ以外はバッファリング無し、Rendezvous)
• Synchronous (バッファリング無し、Rendezvous)
• Ready (バッファリング有り、Eager)注:受信側のシステムバッファを使う、小さいとエラー
• Buffered (ユーザ定義バッファ有り、 Rendezvous)注:ユーザ定義バッファ送信側で設定しサイズの指定が可能
⚫通信モードと protocol の対応例
59
-
int sbuf[2], rbuf[2];
if(rank==0) {sbuf[0] = 10; sbuf[1] = 20;MPI_Bsend( sbuf, 2, MPI_INT, 1, 8, MPI_COMM_WORLD );MPI_Recv( rbuf, 2, MPI_INT, 1, 9, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
} else if(rank==1) {sbuf[0] = 20; sbuf[1] = 30;MPI_Ssend( sbuf, 2, MPI_INT, 0, 9, MPI_COMM_WORLD );MPI_Recv( rbuf, 2, MPI_INT, 0, 8, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
}
① Proc0がBsendを開始② Proc1がSsendを開始③ 添付バッファにバッファリング④ Proc0がBsendから復帰⑤ Proc0がRecvを開始⑥ Proc1がSsendから復帰⑦ Proc1がRecvを開始⑧ Proc0がRecvから復帰⑨ Proc1がRecvから復帰正常終了
• プロセス0でMPI_SendをMPI_Bsendに置き換える.• プロセス1はバッファリングを抑止するためMPI_Ssendを使う.
Proc0 Proc1
Bsend Ssend
① ②
Recv Recv
③
⑥④
⑧ ⑨
⑦⑤
◼通信モードを使ってデッドロックを避ける
60
Example (mode01.c)
-
◼MPI_Buffer_attach/MPI_Buffer_detach/MPI_Bsend/MPI_Ssend
int MPI_Buffer_attach( void* buffer, int size )int MPI_Buffer_detach( void* buffer_addr, int* size ) int MPI_Bsend( const void* buf, int count, MPI_Datatype datatype,
int dest, int tag, MPI_Comm comm )
#define BUFFER_SIZE 10000int bufsize = sizeof(int)*BUFFER_SIZE;
void *buf = malloc((size_t)bufsize);MPI_Buffer_attach(buf, bufsize);
if(rank==0) MPI_Bsend(buf, 5000, MPI_INT, 1, 9, MPI_COMM_WORLD);
else if(rank==1)MPI_Recv(buf, 5000, MPI_INT, 0, 9, MPI_COMM_WORLD);
MPI_Buffer_detach(buf, &bufsize);
⚫ ユーザ定義のバッファを生成・解放する
注:バッファサイズの指定はバイト数!Buffer_detachのsizeはポインタ型!
61
Function prototype
-
int sbuf[2], rbuf[2];
if(rank==0) {sbuf[0] = 10; sbuf[1] = 20;MPI_Send( sbuf, 2, MPI_INT, 1, 9, MPI_COMM_WORLD ); MPI_Recv( rbuf, 2, MPI_INT, 2, 7, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
} else if(rank==1) {sbuf[0] = 30; sbuf[1] = 40;MPI_Send( sbuf, 2, MPI_INT, 2, 8, MPI_COMM_WORLD );MPI_Recv( rbuf, 2, MPI_INT, 0, 9, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
} else if(rank==2) {sbuf[0] = 50; sbuf[1] = 60;MPI_Send( sbuf, 2, MPI_INT, 0, 7, MPI_COMM_WORLD );MPI_Recv( rbuf, 2, MPI_INT, 1, 8, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
}
正常終了できるかはバッファリングに依存
⚫ 3プロセスのデータ交換
周期境界の場合
Buffered modeを使う
Proc0 Proc1 Proc2
Send
Recv
Send
Recv
Send
Recv
62
Example (mode02.c)
-
int sbuf[2], rbuf[2];
if(rank==0) {sbuf[0] = 10; sbuf[1] = 20;MPI_Bsend( sbuf, 2, MPI_INT, 1, 9, MPI_COMM_WORLD ); MPI_Recv( rbuf, 2, MPI_INT, 2, 7, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
} else if(rank==1) {sbuf[0] = 30; sbuf[1] = 40;MPI_Ssend( sbuf, 2, MPI_INT, 2, 8, MPI_COMM_WORLD );MPI_Recv( rbuf, 2, MPI_INT, 0, 9, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
} else if(rank==2) {sbuf[0] = 50; sbuf[1] = 60;MPI_Ssend( sbuf, 2, MPI_INT, 0, 7, MPI_COMM_WORLD );MPI_Recv( rbuf, 2, MPI_INT, 1, 8, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
}
添付バッファサイズが十分なら正常終了
• プロセス0でMPI_SendをMPI_Bsendに置き換える.• プロセス1と2はバッファリングを抑止するためMPI_Ssendを使う.
① tag=7の送受信が完了
② tag=8の送受信が完了
③ tag=9の送受信が完了 しかし送受信は逐次的 ⇒ 非効率
int MPI_Ssend( const void* buf, int count, MPI_Datatype datatype,int dest, int tag, MPI_Comm comm )
63
Example (mode03.c)
Function prototype
-
int sbuf[2], rbuf[2];
if(rank==0) {sbuf[0] = 10; sbuf[1] = 20;MPI_Bsend( sbuf, 2, MPI_INT, 1, 9, MPI_COMM_WORLD ); MPI_Recv( rbuf, 2, MPI_INT, 2, 7, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
} else if(rank==1) {sbuf[0] = 30; sbuf[1] = 40;MPI_Bsend( sbuf, 2, MPI_INT, 2, 8, MPI_COMM_WORLD );MPI_Recv( rbuf, 2, MPI_INT, 0, 9, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
} else if(rank==2) {sbuf[0] = 50; sbuf[1] = 60;MPI_Bsend( sbuf, 2, MPI_INT, 0, 7, MPI_COMM_WORLD );MPI_Recv( rbuf, 2, MPI_INT, 1, 8, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
}
添付バッファサイズが十分なら正常終了
• 全てのプロセスでMPI_SendをMPI_Bsendに置き換える..
通信は並列に実行可能しかしメモリコピーの時間が必要
データサイズが大きな場合非効率
⇒ 非閉塞Synchronous通信を使う!64
Example (mode04.c)
-
正常終了(並列的・メモリコピー不要)
◼MPI_Issend
MPI_Request req;int sbuf[2], rbuf[2];
if(rank==0) {sbuf[0] = 10; sbuf[1] = 20;MPI_Issend( sbuf, 2, MPI_INT, 1, 9, MPI_COMM_WORLD, &req ); MPI_Recv( rbuf, 2, MPI_INT, 2, 7, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
} else if(rank==1) {sbuf[0] = 30; sbuf[1] = 40;MPI_Issend( sbuf, 2, MPI_INT, 2, 8, MPI_COMM_WORLD, &req );MPI_Recv( rbuf, 2, MPI_INT, 0, 9, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
} else if(rank==2) {sbuf[0] = 50; sbuf[1] = 60;MPI_Issend( sbuf, 2, MPI_INT, 0, 7, MPI_COMM_WORLD, &req );MPI_Recv( rbuf, 2, MPI_INT, 1, 8, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
} MPI_Wait( &req, MPI_STATUS_IGNORE );
• 全てのプロセスでMPI_SendをMPI_Issendに置き換える.• 後からMPI_Waitで完了させる.
但しリクエストの完了が必要
int MPI_Issend( const void* buf, int count, MPI_Datatype datatype,int dest, int tag, MPI_Comm comm , MPI_Request *request )
65
Example (mode05.c)
Function prototype
-
66
◼完了関数の基本動作
• 閉塞通信では復帰=完了なので不要• 非閉塞通信(持続通信も含む)では
通信ハンドル(request)を生成して即座に復帰する• 通信の完了には request を引数にWaitまたはTest関数を呼ぶ• 通信が完了していれば request から status が生成される• recv側は MPI_Get_count で status から受信データ数を得る• send側の request は MPI_request_free ですぐに解放しても良い
⚫ Wait関数 (MPI_Wait, MPI_Waitall, MPI_Waitany, MPI_Waitsome)• Wait は request の完了まで待ち、完了後に status を返す• Wait の動作は非局所的(他プロセスに依存)で blocking
⚫ Test関数 (MPI_Test, MPI_Testall, MPI_Testany, MPI_Testsome)• Test は request の状態を flag に返して復帰する• status の値は flag=true の時には正常値、
flag=false の時は不定値となる• Test の動作は局所的(自プロセスで完結)で nonblocking
-
int MPI_Request int int int MPI_StatusMPI_Wait ( ----- request, ----- ----- ----- *status )MPI_Test ( ----- request, ----- *flag, ----- *status )
MPI_Wait_all ( count, requests[], ----- ----- ----- statuses[] )MPI_Test_all ( count, requests[], ----- *flag, ----- statuses[] )
MPI_Wait_any ( count, requests[], ----- ----- *index, *status )MPI_Test_any ( count, requests[], ----- *flag, *index, *status )
MPI_Wait_some ( incount, requests[], *outcount, ----- indices[], statuses[] )MPI_Test_some ( incount, requests[], *outcount, ----- indices[], statuses[] )
67
◼完了関数の引数一覧
• all は requests[0, …, count-1]全てについて Wail/Test• any は requests[0, …, count-1]のどれか一つ(indexにその番号を返す)• some は requests[0, …, incount-1]の内 *outcount 個、番号は indices• Test_some は all, any と仕様が違うので注意• 既に完了した request を再度 Wait, Test しても問題ない• 非閉塞集団通信の場合の status の内容は不定(記述無し)
-
68
◼完了関数の引数一覧(続き)
• Cancel は request を取り消すが、その後に MPI_Wait か MPI_Testで完了するか、MPI_Request_free で解放する必要がある
• 非閉塞の要素通信は cancel できるが、集団通信はできない• Cancel は重い処理なのでなるべく使用しない• Request_free は request の割当てを解除(deallocate)する
解放された request は MPI_Wait や MPI_Test の引数に指定できない• Request_get_status は request が完了したかどうかを調べ
結果を flag と status に返す
MPI_Request int MPI_StatusMPI_Cancel ( *request, ----- ----- )MPI_Request_free ( *request, ----- ----- )MPI_Request_get_status ( request, *flag, *status )
const MPI_Status intMPI_Test_cancelled ( *status, *flag )
• statusを調べて対応する通信が cancel に成功したかを flag に返す
-
◼複合通信 (Combined communication)
int MPI_Sendrecv ( const void *sendbuf, int sendcount, MPI_Datatype sendtype,int dest, int sendtag, void *recvbuf, int recvcount, MPI_Datatype recvtype,int source, int recvtag, MPI_Comm comm, MPI_Status *status )
int MPI_Sendrecv_replace ( void *buf, int count, MPI_Datatype datatype,int dest, int sendtag, int source, int recvtag, MPI_Comm comm, MPI_Status *status )
69
Function prototype
⚫ 自プロセス内の送信と受信を一つの関数で実行する
一対の送受信ではないことに注意
⚫ 通信相手側は通常の send, recv で受けても良い
⚫ バッファを共通化した関数も用意されている (MPI_Sendrecv_replace)
⚫ 非閉塞版はない (デッドロックしないので必要ない)
int sbuf[2], rbuf[2], buf[2];int left, right;MPI_sendrecv( sbuf, 2, MPI_INT, left, 9,
rbuf, 2, MPI_INT, right, 8, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
MPI_sendrecv_replace( buf, 2, MPI_INT, left, 9, right, 8, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
Example (-----.c)
• replaceの場合はデータ型とサイズが送信と受信で同じでなければならない
-
int MPI_Send_init( const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request )
int MPI_Ssend_init( const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request )
int MPI_Bsend_init( const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request )
int MPI_Rsend_init( const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request )
int MPI_Recv_init( const void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Request *request )
int MPI_Start( MPI_Request *request ) int MPI_Startall( int count, MPI_Request array_of_requests [] )
◼持続通信 (Persistent communication)
70
Function prototype
⚫ 最初に作った request を何度も再利用する
⚫ 通信開始は request を start させるだけでよい
⚫ 非閉塞通信と同様に test, wait を呼んで完了させる
-
71
int sbuf[100], rbur[100];MPI_Request req[2];
MPI_Send_init( sbuf, 100, MPI_INT, right, 0, MPI_COMM_WORLD, &(req[0]) ); MPI_Recv_init( rbuf, 100, MPI_INT, left, 0, MPI_COMM_WORLD, &(req[1]) );
for(int n=0; n
-
⚫ データを一斉に隣のプロセスに送るシフト通信の場合両端のプロセスは送信または受信のみとなる
⚫ 送受信関数の dest/source に MPI_PROC_NULL を指定すると通信せずに復帰する
⚫ これを使うとシフト通信のプログラムが少し見易くなる
◼MPI_PROC_NULLを使う
int sbuf[10], rbuf[10];int size, rank;MPI_Comm_size( MPI_COMM_WORLD, &size );MPI_Comm_rank( MPI_COMM_WORLD, &rank );
int dest = ( rank0 ? rank-1 : MPI_PROC_NULL );
MPI_Send( sbuf, 10, MPI_INT, dest, 1, MPI_COMM_WORLD );MPI_Recv( rbuf, 10, MPI_INT, src , 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
Proc0 Proc1 Proc2
Send
Recv
Send
Recv
Send
Recv
MPI_PROC_NULL
MPI_PROC_NULL 72
-
⚫ マスターワーカモデル(Master-Worker model)では各ワーカがマスターに送ってくるタイミングが不定
⚫ プロセス番号とタグを指定して受信する方法では無駄な待ちが発生する ⇒ 順不同にデータを受け取りたい
⚫ 受信関数の source, tag に MPI_ANY_SOURCE, MPI_ANY_TAG を指定すると到着した順(First come, first served)に受信する
⚫ countは実際に受信するデータより大きな値でも可
⚫ しかし送信されたデータのサイズが予め分らないので受信バッファ rbuf のサイズを決められない
不足すると segmentation fault 等を発生しエラー終了
⇒ MPI_Probe で事前に status のみ取得する!
◼MPI_ANY_SOURCE/MPI_ANY_TAGを使う
int rbuf[100];int count;
if( master )MPI_Recv( rbuf, count, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG,
MPI_COMM_WORLD, MPI_STATUS_IGNORE );
73
-
⚫ MPI_Probe はデータを受け取らず status のみ受け取る
⚫ MPI_Iprobeはその非閉塞版、flagはtrueまたはfalseを返す
⚫ MPI_Get_countは status から受信データのサイズを抽出する
◼MPI_Probe/MPI_Iprobe/MPI_Get_count
int MPI_Probe( int source, int tag, MPI_Comm comm , MPI_Status *status )int MPI_Iprobe( int source, int tag, MPI_Comm comm , int *flag, MPI_Status *status )int MPI_Get_count( const MPI_Status *status, MPI_Datatype datatype, int *count )Int MPI_Alloc_mem( MPI_Aint size, MPI_Info info, void *baseptr )
int *rbuf;int count;MPI_Status status;if( master ){
MPI_Probe( MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status );MPI_Get_count( &status, MPI_INT, &count );MPI_Alloc_mem( count*sizdof(int), MPI_INFO_NULL, rbuf );MPI_Recv( rbuf, count, MPI_INT, status.MPI_SOURCE, status.MPI_TAG,
MPI_COMM_WORLD, MPI_STATUS_IGNORE );}
注:statusはMPI_SOURCE, MPI_TAG, MPI_ERROR等をメンバとして持つ構造体
但しProbe / Iprobeはマルチスレッド実行時に問題あり74
-
⚫MPI関数各論
1. 集団通信
2. 要素通信
3. 片側通信
4. マルチスレッド
5. 派生データ型
6. ファイルI/O
7. コミュニケータ・トポロジー
8. プロセス生成
9. その他
75
-
◼片側通信とは
⚫ 各プロセスのメモリ上にウィンドウ(window)と呼ばれる公開領域を設定し、この領域に対して送受信する
◼注意点
⚫ ウィンドウ初期化、送受信、同期の3ステップがある
⚫ 送受信関数の呼び出しは送信側または受信側のみ
⚫ 比較・演算を組合わせた送受信も可能
⚫ 送受信関数には request を返すものもある
⚫ 通信は request が無い場合でも同期が必要
⚫ 同期方法には2種類の能動的同期(active target
synchronization)と1種類の受動的同期(passive target
synchronization)がある
76
-
◼片側通信関数一覧
Nonlocal Local
Communi-
cation
Initialization
MPI_Rput
MPI_Rget
MPI_Raccumurate
MPI_Rget_accumurate
MPI_Put
MPI_Get
MPI_Accumurate
MPI_Get_accumurate
MPI_Fetch_and_op
MPI_Compare_and_swap
77
MPI_Win_create
MPI_Win_create_dynamic + MPI_Win_attach/detach
MPI_Win_allocate
MPI_Win_allocate_shared*
MPI_Win_free
* MPI_Win_allocate_shared, MPI_Win_shared_queryはマルチスレッドのページで説明する
Query MPI_Win_shared_query*
-
◼片側通信関数一覧(続)
Synchro-
nization
78
MPI_Win_Fence
MPI_Win_Start
MPI_Win_Post
MPI_Win_Complete
MPI_Win_Wait
MPI_Win_Test
Active target Passive target
MPI_Win_lock
MPI_Win_lock_all
MPI_Win_unlock
MPI_Win_unlock_all
MPI_Win_flush
MPI_Win_flush_all
MPI_Win_flush_local
MPI_Win_flush_local_all
MPI_Win_sync
Group MPI_Win_get_group*
InfoMPI_Win_set_info*
MPI_Win_get_info*
* MPI_Win_get_group, MPI_Win_set_info, MPI_Win_get_infoの説明は省略
-
79
◼初期化関数の引数
• どちらも local call、size はバイト数指定• base, size はプロセス毎に異なってもよい• MPI_Win_detach の引数 *base は正確には const void
MPI_Win void MPI_AintMPI_Win_attach ( win *base size )MPI_Win_detach ( win *base ----- )
• 全て collective call である、size と disp_unit はバイト数を指定する• base, size, disp_unit, info はプロセス毎に異なってもよい• MPI_Win_create は base から始まる size バイトの領域を設定する
size==0 の場合は base に MPI_BOTTOM を指定してもよい• MPI_Win_allocate はシステムが size バイトの領域を確保、その先頭番地を
baseptr で返す(symmetric allocation できれば create より scalability 良い可能性)• MPI_Win_create_dynamic は領域を確保せずにウィンドウを作成する
通信する前に MPI_Win_attach でメモリ割当てが必要MPI_Win_detach で解放してから再度割り当てることができる
function Void MPI_Aint int MPI_Info MPI_Comm void MPI_Win
MPI_Win_create *base size disp_unit info comm ----- *win
MPI_Win_create_dynamic ----- ----- ----- info comm ----- *win
MPI_Win_allocate ----- size disp_unit info comm *baseptr *win
MPI_Win_allocate_shared ----- size disp_unit info comm *baseptr *win
MPI_Free ----- ----- ----- ----- ----- ----- *win
-
80
◼送受信関数の引数
• 関数の呼出し側(caller)プロセスを origin、相手側(callee)を target と呼ぶ• データ移動の始点を source、終点を destination と呼ぶ
➢ MPI_Put ⇒ origin=source, target=destination➢ MPI_Get ⇒ origin=destination, target=source
• target 側は memory window 内のデータのみアクセス可能、origin 側は制約なし
functionorigin compare result target
void int Datatype void void int Datatype int Aint int Datatype Op Win Request
MPI_Put *addr count type ----- ----- ----- ----- rank disp count type ----- win -----
MPI_Get *addr count type ----- ----- ----- ----- rank disp count type ----- win -----
MPI_Accumulate *addr count type ----- ----- ----- ----- rank disp count type op win -----
MPI_Get_accumulate *addr count type ----- *addr count type rank disp count type op win -----
MPI_Fetch_and_op *addr ----- ----- ----- *addr ----- type rank disp ----- ----- op win -----
MPI_Compare_and_swap *addr ----- ----- *addr *addr ----- type rank disp ----- ----- ----- win -----
MPI_Rput *addr count type ----- ----- ----- ----- rank disp count type ----- win *request
MPI_Rget *addr count type ----- ----- ----- ----- rank disp count type ----- win *request
MPI_Raccumulate *addr count type ----- ----- ----- ----- rank disp count type op win *request
MPI_Rget_accumulate *addr count type ----- *addr count type rank disp count type op win *request
c.f. abbreviations: MPI_Datatype => Datatype, MPI_Aint => Aint, MPI_Op => Op, MPI_Win => Win, MPI_Request => Request
また origin と compare の void は正確には const void
-
81
◼送受信関数の引数(続)
• 動作は非閉塞的であり、必ず同期が必要• Put は origin の内容を target に代入、Get は target の内容を origin に代入
• Op は既定義の操作のみ(ユーザ定義は不可)、 origin と target の間で演算• Accumurate は演算結果を target に格納
Op = MPI_REPLACE の場合は origin の内容を target に代入
• Get_accumulate は target の内容を result に保存後、演算結果を target に格納Op = MPI_REPLACE の場合は origin の内容を target に代入Op = MPI_NO_OP の場合は origin と target の内容は不変
• Fetch_and_op は target は Get_accumulate でデータが1要素の場合に特化
• Compare_and_swap も1要素のみ対象、target の内容を result に保存し、compare と target の内容が等しい場合のみ origin の内容を target に代入
• Rのついた関数は request を返し MPI_Test, MPI_Wait 等で local に同期できる(但し、passive target synchronization でしか使えない)
-
82
◼同期関数の引数function int int MPI_Group int MPI_Win int
MPI_Win_fence ----- ----- ----- assert win -----
MPI_Win_start ----- ----- group assert win -----
MPI_Win_complete ----- ----- ----- ----- win -----
MPI_Win_post ----- ----- group assert win -----
MPI_Win_wait ----- ----- ----- ----- win -----
MPI_Win_test ----- ----- ----- ----- win “flag
MPI_Win_lock type rank ----- assert win -----
MPI_Win_unlock ----- rank ----- ----- win -----
MPI_Win_flush ----- rank ----- ----- win -----
MPI_Win_flush_local ----- rank ----- ----- win -----
MPI_Win_lock_all ----- ----- ----- assert win -----
MPI_Win_unlock_all ----- ----- ----- ----- win -----
MPI_Win_flush_all ----- ----- ----- ----- win -----
MPI_Win_flush_local_all ----- ----- ----- ----- win -----
MPI_Win_sync ----- ----- ----- ----- win -----
-
83
◼同期の方法
⚫ Active target:• origin と target の両側で同期関数を呼ぶ• origin は access epoch の間だけ通信できる• target は exposure epoch の間だけウィンドウを公開する• 以下の2種類の方法がある
a. MPI_Win_fenceb. MPI_Win_start + MPI_Win_complete / MPI_Win_post + MPI_Win_wait
⚫ Passive target:• origin 側のみが同期関数を呼ぶ
• origin は access epoch の間だけ通信できる• target は同期関数を呼ばないので epoch はない• 方法は下記1種類のみ
a. MPI_Win_lock + MPI_Win_unlock,(MPI_Win_lock_all + MPI_Win_unlock_all)
-
84
◼ Active target (collective synchronization)
⚫ MPI_Win_fence を呼ぶ⚫ MPI_Win_fence は collective call である⚫ MPI_Win_fence と MPI_Win_fence の間が epoch になり、
直前のMPI_Win_fenceまでの通信を完了する⚫ 通信関数を呼ぶと origin なので access epoch と解釈される⚫ 通信関数を呼ばない場合は exposure epoch と解釈される⚫ コミュニケータ内の全プロセスが全ウィンドウにアクセス可能⚫ 通信相手の数が多く、しかも頻繁に変わる場合に使う
MPI_Win_fence
MPI_Put
MPI_Get
MPI_Win_fence
MPI_Win_fence
MPI_Win_fence
origin process target process
buffer
window
access epoch exposure epochbuffer
-
85
◼ Active target (restricted synchronization)
⚫ origin 側は MPI_Win_start/MPI_Win_complete 、target 側は MPI_Win_post/MPI_Win_wait を呼ぶ
⚫ 通信する相手は MPI_Win_start で group を指定する⚫ ウィンドウを公開する相手は MPI_Win_post で group を指定する⚫ target 側が post するまで通信は始まらない⚫ origin 側が complete した後 wait が復帰する (strong synchronization)⚫ バッファリングする実装ではこの制約は無い (weak synchronization)⚫ 通信相手の数が少なく、しかも固定的な場合に使う
MPI_Win_start( group, … )
MPI_Put
MPI_Get
MPI_Win_complete
MPI_Win_post( group, … )
MPI_Win_wait
origin process target process
buffer
window
access epoch exposure epochbuffer
*MPI_Win_test はMPI_Win_wait の非閉塞版
-
86
◼ Passive target (lock synchronization)
⚫ origin 側は MPI_Win_lock/MPI_Win_unlockまたは MPI_Win_lock_all/MPI_Win_unlock_all を呼ぶ
⚫ 通信する相手は lock/unlock では rank のみlock_all/unlock_all では win の全プロセス
⚫ lock_all/unlock_all は collective call ではない⚫ Target側のウィンドウを複数のプロセスが共用する場合は
type = MPI_LOCK_SHARED 、1つのプロセスが排他的(atomic)に使用する場合は type = MPI_LOCK_EXCLUSIVE を指定する
MPI_Win_lock( type, rank, … )
MPI_Put
MPI_Get
MPI_Win_unlock( rank, … )
origin process target process
buffer
window
access epoch buffer
• 一般に flush は待ち状態の送受信を強制的に完了させる
• MPI_Win_flush は rank に対する送受信、MPI_Win_fllush_all は win 内の全送受信
を origin, target の両側で完了させる• MPI_Win_flush_local, MPI_Win_flush_local_all
は origin 側のみ完了させる• MPI_Win_sync はウィンドウを整合化し
epoch を仕切りなおす
-
⚫MPI関数各論
1. 集団通信
2. 要素通信
3. 片側通信
4. マルチスレッド
5. 派生データ型
6. ファイルI/O
7. コミュニケータ・トポロジー
8. プロセス生成
9. その他
87
-
◼マルチスレッド時のMPI関数実行
⚫ MPIではマルチスレッド時の実行に関して
低い順に以下の4つのレベルが設定されている
⚫ 実行環境により設定可能なレベルは異なる
• MPI_THREAD_SINGLE
シングルスレッド時のみ実行可能
• MPI_THREAD_FUNNELED
マルチスレッド時はマスタースレッドのみ実行可能
• MPI_THREAD_SERIALIZED
マルチスレッド時は同時に一つのスレッドのみ実行可能*
• MPI_THREAD_MULTIPLE
マルチスレッド時も制約なしにどのスレッドも実行可能
*スレッド間の排他制御はユーザの責任
88
-
⚫ レベル指定付き初期化
int MPI_Init_thread( int *argc, char ***argv, int required, int *provided )
int MPI_Query_thread( int *provided )int MPI_Is_thread_main( int *flag )
int provided;MPI_Init_thread( argc, argv, MPI_THREAD_MULTIPLE, &provided );if( provided!=MPI_THREAD_MULTIPLE )
printf( “MPI_THREAD_MULTIPLE is not supported!¥n” );
int provided, flag;MPI_Query_thread( &provided );if( provided!=MPI_THREAD_MULTIPLE )
printf( “MPI_THREAD_MULTIPLE is not supported!¥n” );MPI_Is_thread_main( &flag );if( flag )
printf( “I am master thread!¥n” );
注:MPI_Init 同様、 argc, argv は省略可能requiredはプロセス毎に異なる値でも可
Function prototype
Example (thread01.c)
Function prototype
Example (thread02.c)
⚫ 問い合わせ関数
89
-
⚫スレッド共有記憶の設定
90
MPI_Comm oldcomm, newcomm;MPI_Info info = MPI_INFO_NULL;int type = MPI_COMM_TYPE_SHARED;int key = ;MPI_Comm_split_type( oldcomm, type, key, info, newcomm );
Example (-----.c)
int MPI_Comm_split_type( MPI_Comm comm, int split_type, int key, MPI_Info info, MPI_Comm newcomm )
Function prototype
• 共有記憶領域毎に分離したコミュニケータを生成
MPI_Comm_create_group ( comm, group, tag, *newcomm )
・comm はイントラのみ・group 内で collective call・tag はマルチスレッド実行の時に使う
-
⚫スレッド共有記憶の設定
91
MPI_Comm oldcomm, newcomm;MPI_Info info = MPI_INFO_NULL;int type = MPI_COMM_TYPE_SHARED;int key = ;MPI_Comm_split_type( oldcomm, type, key, info, newcomm );
Example (-----.c)
int MPI_Win_allocate_shared( MPI_Aint size, int disp_unit, MPI_Info info, MPI_Comm comm, void *baseptr, MPI_Win win )
Function prototype
• comm のプロセスが共有する size バイトのメモリウィンドウ win を割当てそのポインタを baseptr に返す
int MPI_Win_shared_query( MPI_Win win, int rank, MPI_Aint size, int disp_unit, void *baseptr )
Function prototype
• MPI_Win_allocate_shared で割当てたメモリウィンドウの情報を返す
-
◼MPI_Mprobe/MPI_Mrecv/MPI_Improbe/MPI_Imrecv
⚫ 複数のスレッドがMPI_Probeを実行した時、タイミングによって後続の MPI_Recvがデータを取り違える可能性がある
⚫ MPI_THREAD_MULTIPLE環境では thread safe な MPI_Mprobeと MPI_Mrecv (Matching Probe/Recv)を使う
⚫ MPI_Improbe と MPI_Imrecv はその非閉塞バージョン
⚫ ProbeとRecvは閉塞と非閉塞を組み合わせてもよい
int MPI_Mprobe( int source, int tag, MPI_Comm comm,MPI_Message *message, MPI_Status *status )
int MPI_Mrecv( void *buf, int count, MPI_Datatype datatype, MPI_Message *message, MPI_Status *status )
int MPI_Improbe( int source, int tag, MPI_Comm comm, int *flag,MPI_Message *message, MPI_Status *status )
int MPI_Imrecv( void *buf, int count, MPI_Datatype datatype, MPI_Message *message, MPI_Request *request )
注:MPI_Improbeの引数にはrequestが無い(Wait/Test不要)、代わりにflagがある(受信可能なデータの有無)。またMPI_Imrecvの引数にはstatusが無い(Wait/Testで取得)
Function prototype
92
-
int *rbuf;int count;MPI_Status status;MPI_Message message;if( master ){
MPI_Mprobe( MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD,&message, &status );
MPI_Get_count( &status, MPI_INT, &count );MPI_Alloc_mem( count*sizdof(int), MPI_INFO_NULL, rbuf );MPI_Mrecv( rbuf, count, MPI_INT, &message, MPI_STATUS_IGNORE );
}
int *rbuf;int count, flag;MPI_Status status;MPI_Message message;MPI_Request request;if( master ){
MPI_Improbe( MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag,&message, &status );
if( !flag ) continue;MPI_Get_count( &status, MPI_INT, &count );MPI_Alloc_mem( count*sizdof(int), MPI_INFO_NULL, rbuf );MPI_Imrecv( rbuf, count, MPI_INT, &message, &request );MPI_Wait( &request, MPI_STATUS_IGNORE );
}
Example (mprobe.c)
Example (improbe.c)
93
-
⚫MPI関数各論
1. 集団通信
2. 要素通信
3. 片側通信
4. マルチスレッド
5. 派生データ型
6. ファイルI/O
7. コミュニケータ・トポロジー
8. プロセス生成
9. その他
94
-
◼派生データ型とは
⚫ 基本型を組み合わせてユーザが定義する構造を持ったデータ型
⚫ ギャップを挟んだデータや複数の型をメンバーにもつ構造体なども定義できる
⚫ さらに派生データ型を組み合わせた派生データ型も定義可能
◼注意点
⚫ 関数名に一部整合的でないものが存在する(動詞Createが欠けている)
⚫ 長さの指定が要素数の場合とバイト数の場合がある⚫ ギャップを間引いて圧縮したい場合は MPI_Pack で
圧縮し MPI_Unpack で復元する
95
-
◼派生データ型関数一覧
Definition
96
MPI_Type_contiguous
MPI_Type_vector
MPI_Type_create_indexed_block
MPI_Type_indexed
MPI_Type_create_struct
MPI_Type_create_subarray*
MPI_Type_create_darray*
MPI_Type_create_resized*
Elements Bytes
MPI_Type_create_hvector
MPI_Type_create_hindexed_block
MPI_Type_create_hindexed
management
MPI_Type_commit
MPI_Type_free
MPI_Type_dup*
MPI_Get_address*
* MPI_Type_create_subarray, MPI_Type_create_darray はファイルI/Oで説明するMPI_Type_create_resized, MPI_Type_dup, MPI_Get_addressの説明は省略
-
◼派生データ型関数一覧(続)
97
Information
MPI_Type_size
MPI_Type_get_extent*
MPI_Type_get_true_extent*
MPI_Type_get_elements*
MPI_Type_get_envelope*
MPI_Type_get_contents*
int/Aint
MPI_Type_size_x*
MPI_Type_get_extent_x*
MPI_Type_get_true_extent_x*
MPI_Type_get_elements_x*
MPI_Count
MPI_Unpack
MPI_Unpack_external*
MPI_Pack
MPI_Pack_size
MPI_Pack_external*
MPI_Pack_external_size*
Deposit Withdrawal
Pack/Unpack
* MPI_Type_size, MPI_Pack , MPI_Unpack , MPI_Pack_size以外の説明は省略
-
◼MPI_Type_contiguous
int MPI_Type_contiguous( int count , MPI_Datatype oldtype , MPI_Datatype *newtype )
MPI_Datatype newtype;
MPI_Type_contigous(4, MPI_INT, &newtype);
Function prototype
Example (-----.c)
• oldtype のデータを count 個連接した型を定義する
98
newtype = int int int int
-
◼MPI_Type_vector/MPI_Type_create_hvector
int MPI_Type_vector(int count , int blocklength, int stride,MPI_Datatype oldtype, MPI_Datatype *newtype)
int MPI_Type_create_hvector(int count , int blocklength, MPI_Aint stride, MPI_Datatype oldtype, MPI_Datatype *newtype)
MPI_Datatype newtype;
MPI_Type_vector(3, 2, 4, MPI_INT, &newtype);
Function prototype
Example (-----.c)
• 連続する blocklengh 個のデータを count 回繰り返す• ブロック間の距離は stride で指定する• stride の単位は vector は要素数、hvector はバイト数(h は heterogeneous の頭文字)
99
newtype = int int int int int int int int int int
blocklength
count
stride
-
◼MPI_Type_create_indexed_block/MPI_Type_create_hindexed_block
int MPI_Type_create_indexed_block(int count , int blocklength, const int array_of_displacements[], MPI_Datatype oldtype, MPI_Datatype *newtype)
int MPI_Type_create_hindexed_block(int count , int blocklength, const MPI_Aint array_of_displacements[], MPI_Datatype oldtype, MPI_Datatype *newtype)
MPI_Datatype newtype;
const int displacements = {4,3,0};MPI_Type_indexed_block(3, 2, displacements, MPI_INT, &newtype);
Function prototype
Example (-----.c)
• MPI_Type_vector で stride の指定を可変にしたもの• array_of_displacements の単位は indexed_block は要素数、hindexed_block はバイト数
100
newtype = int int int int int int int int int
blocklength
count
displacements[0] displacements[1]
-
◼MPI_Type_indexed/MPI_Type_create_hindexed
int MPI_Type_indexed(int count , const int array_of_blocklengths[], const int array_of_displacements[], MPI_Datatype oldtype, MPI_Datatype *newtype)
int MPI_Type_create_hindexed(int count , const int array_of_blocklengths[], const MPI_Aint array_of_displacements[], MPI_Datatype oldtype, MPI_Datatype *newtype)
MPI_Datatype newtype;
const int blocklengths = {2,1,3};const int displacements = {4,3,0};
MPI_Type_indexed(3, blocklengths, displacements, MPI_INT, &newtype);
Function prototype
Example (-----.c)
• MPI_Type_vector で blocklengh, stride の指定を可変にしたもの• array_of_displacements の単位は indexed は要素数、hindexed はバイト数
101
newtype = int int int int int int int int int int
blocklengths[0]
count
displacements[0]
blocklengths[1] blocklengths[2]
displacements[1]
-
◼MPI_Type_create_struct
int MPI_Type_create_struct(int count , const int array_of_blocklengths[], const MPI_Aint array_of_displacements[], MPI_Datatype array_of_types[], MPI_Datatype *newtype)
MPI_Datatype newtype;
const int blocklengths = {2,1,3};const int displacements = {16,24,0};
MPI_Datatype types = {MPI_INT, MPI_FLOAT, MPI_CHAR};
MPI_Type_indexed(3, blocklengths, displacements, types, &newtype);
Function prototype
Example (-----.c)
• MPI_Type_vector で blocklengh, stride, oldtype の指定を可変にしたもの• array_of_displacements の単位はバイト数
102
newtype =
blocklengths[0]
count
displacements[0]
blocklengths[1] blocklengths[2]
displacements[1]
int int float char char char
-
◼MPI_Type_commit
int MPI_Type_commit(MPI_Datatype *datatype)
MPI_Datatype newtype;
MPI_Type_commit(&newtype);
Function prototype
Example (-----.c)
• 派生データ型を通信に使えるよう有効化する
103
◼MPI_Type_free
int MPI_Type_free(MPI_Datatype *datatype)
MPI_Datatype newtype;
MPI_Type_free(&newtype);
Function prototype
Example (-----.c)
• 派生データ型を解放する(deallocateする)
* MPI_Type_create_subarray, MPI_Type_create_darray はファイルI/Oのページで説明する
-
◼MPI_Pack
int MPI_Pack(const void *inbuf, int incount, MPI_Datatype datatype, void *outbuf, int outsize, int *position, MPI_Comm comm)
Function prototype
• inbuf から始まる incount 個のdatatype 型データを outbuf から始まる長さ outsize バイトのバッファに push した後にカレントポインタ position を返す
104
float in0[2] = {0.1, 0.2};
int in1[3] = {11, 12, 13};int position = 0;
char out[1000];
MPI_Pack(in0, 2, MPI_FLOAT, out, 1000, &position, MPI_COMM_WORLD);MPI_Pack(in1, 3, MPI_INT, out, 1000, &position, MPI_COMM_WORLD);
MPI_Send(out, position, MPI_PACKED, 1, 9, MPI_COMM_WORLD);
Example (pack.c)
outbuf = float int intfloat int
1000 bytes
incount
float floatinbuf =
incount
intinbuf = int int
position position position
-
Function prototype
105
◼MPI_Unpack
• inbuf から始まる長さ insize のバッファのカレントポインタ position から、outcount 個のdatatype 型データを pop して outbuf に格納する
• MPI_recv の recvsize が position でないことに注意
int MPI_Unpack(const void *inbuf, int insize, int *position, void *outbuf, int outcount, MPI_Datatype datatype, MPI_Comm comm)
float out0[2];
int out1[3];int position = 0;
char in[1000];
MPI_Recv(in, 1000, MPI_PACKED, 0, 9, MPI_COMM_WORLD, MPI_STATUS_IGNORE);MPI_Unpack(in, 1000, &position, out0, 2, MPI_FLOAT, MPI_COMM_WORLD);
MPI_Unpack(in, 1000, &position, out1, 3, MPI_INT, MPI_COMM_WORLD);
Example (pack.c)
outcount
float floatoutbuf =
outcount
intoutbuf = int int
inbuf = float int intfloat int
1000 bytes
position position position
-
◼MPI_Type_size
int MPI_Type_size(MPI_Datatype datatype, int *size)
MPI_Datatype mytype;
int size;MPI_Type_size(mytype, &size);
Function prototype
Example (-----.c)
• datatype 型の大きさをバイト数で size に返す
106
◼MPI_Pack_size
int MPI_Pack_size(int incount, MPI_Datatype datatype, MPI_Comm comm, int *size)
MPI_Datatype mytype;
int size;MPI_Pack_size(10, mytype, MPI_COMM_WORLD, &size);
Function prototype
Example (-----.c)
• incount 個の datatype 型データを pack するために必要なバイト数を size に返す
-
⚫MPI関数各論
1. 集団通信
2. 要素通信
3. 片側通信
4. マルチスレッド
5. 派生データ型
6. ファイルI/O
7. コミュニケータ・トポロジー
8. プロセス生成
9. その他
107
-
#define BUFSIZE 100int main( int argc, char *argv[] ){
int rank, buf[BUFSIZE];char fname[128];
MPI_Init( &argc, &argv );MPI_Comm_rank( MPI_COMM_WORLD, &rank );sprintf( fname, “myfile%.4d”, rank );
FILE *fp = fopen( fname, “wb” );fwrite( buf, sizeof(int), BUFSIZE, fp );fclose( fp );
MPI_Finalize();return 0;
}
b. 書式無し
108
a. 書式付き
#define BUFSIZE 100int main( int argc, char *argv[] ){
int rank, buf[BUFSIZE];char fname[128];
MPI_Init( &argc, &argv );MPI_Comm_rank( MPI_COMM_WORLD, &rank );sprintf( fname, “myfile%.4d”, rank );
FILE *fp = fopen( fname, “wt” );for( int i=0; i
-
109
◼MPI_I/Oを用いる場合
⚫ プロセス毎にファイルを作る (MPI_COMM_SELF)
⚫ 書式無し(unformatted file)のみ可能
#define BUFSIZE 100int main( int argc, char *argv[] ){
int rank, buf[BUFSIZE];char fname[128];
MPI_Init( &argc, &argv );MPI_Comm_rank( MPI_COMM_WORLD, &rank );sprintf( fname, “myfile%.4d”, rank ); // myfile0000, myfile0001, myfile0002 …
MPI_File fp;MPI_File_open( MPI_COMM_SELF, fname, MPI_MODE_WRONLY|MPI_MODE_CREATE,
MPI_INFO_NULL, &fp );MPI_File_write( fp, buf, BUFSIZE, MPI_INT, MPI_STATUS_IGNORE );MPI_File_close( fp );
MPI_Finalize();return 0;
}
c. 書式無し分離ファイル
-
110
⚫ コミュニケータ内に1つだけファイルを作る (MPI_COMM_WORLD)
⚫ 書式無し(unformatted file)のみ可能
⚫ プロセス毎に書込み位置を指定する (MPI_File_seek)
#define BUFSIZE 100int main( int argc, char *argv[] ){
int rank, buf[BUFSIZE];char fname[128];
MPI_Init( &argc, &argv );sprintf( fname, “myfile”, rank ); // myfile only
MPI_File fp;MPI_File_open( MPI_COMM_WORLD, fname, MPI_MODE_WRONLY|MPI_MODE_CREATE,
MPI_INFO_NULL, &fp );MPI_File_seek( fp, rank*BUFSIZE, MPI_SEEK_SET );MPI_File_write( fp, buf, BUFSIZE, MPI_INT, MPI_STATUS_IGNORE );MPI_File_close( fp );
MPI_Finalize();return 0;
}
d. 書式無し単一ファイル (single file)
-
◼ファイル操作関数
111
MPI_File_open, MPI_File_close, MPI_File_delete,
MPI_File_seek, MPI_File_seek_shared,
MPI_File_get_position, MPI_File_get_position_shared,
MPI_File_set_view, MPI_File_get_view,
MPI_File_set_size, MPI_File_get_size, MPI_File_preallocate,
MPI_File_set_info, MPI_File_get_info,
MPI_File_get_amode,
MPI_File_get_group,
MPI_File_get_byte_offset,
MPI_File_set_atomicity, MPI_File_get_atomicity, MPI_File_sync,
MPI_File_get_type_extent, MPI_Register_datarep,
注:ファイルはindividual file pointerとshared file pointerを持つ
-
⚫ 各プロセスが書き込み可能なファイルの領域
⚫ ファイル先頭からdispバイト後に置かれるfiletypeの繰り返し
⚫ dispとfiletypeはプロセス毎に異なってもよい
⚫ filetypeはetype(char, int, float etc.)の列と空白領域を含む
⚫ MPI_File_set_viewで設定する
112
◼ファイルビュー
etype filetype
lb(bytes)
extent(bytes)hole
hole
fileviewdisp
file
-
◼ 個別ファイルポインタ (individual file pointer)
◼ 陽的オフセット (explicit offset)
◼ 共有ファイルポインタ (shared file pointer)
113
独立I/O (independent I/O)
集団I/O (collective I/O)
分割集団I/O (split collective I/O)
◼データアクセス関数
Blocking Nonblocking
Read
Write
MPI_File_read
MPI_File_read_at
MPI_File_read_shared
MPI_File_read_all
MPI_File_read_at_all
MPI_File_read_ordered
MPI_File_write
MPI_File_write_at
MPI_File_write_shared
MPI_File_write_all
MPI_File_write_at_all
MPI_File_write_ordered
MPI_File_iread
MPI_File_iread_at
MPI_File_iread_shared
MPI_File_read_all_begin/end
MPI_File_read_at_all_begin/end
MPI_File_read_ordered_begin/end
MPI_File_iwrite
MPI_File_iwrite_at
MPI_File_iwrite_shared
MPI_File_write_all_begin/end
MPI_File_write_at_all_begin/end
MPI_File_write_ordered_begin/end
-
114
◼ 個別ファイルポインタ各プロセスが自身のファイルポインタを持つ(順方向に自動更新)
◼ 陽的オフセットファイルポインタを用いず直接に任意の位置を指定する
◼ 共有ファイルポインタコミュニケータ内のプロセスは一つのファイルポインタを共有する
注:ファイルポインタは最後に読書きした位置を保持する
file
process0 process2 process1
file
process0
seekprocess2process1
seekseek
注:MPI_File_seek で位置を指定する
file
process0
file
process2
file
process1
tim
e
注:複数のプロセスが同時に読書きできない、全てのプロセスが同じファイルビューを持っていないとエラー
-
115
*独立I/Oよりも集団I/Oの方が読書きが速い(数倍~数十倍速い