クラスタとメッセージ�パッシング型プログラム �
慶應義塾大学理工学部
天野英晴
hunga@am.ics.keio.ac.jp
NORA/NORMA
データセンターなどで要求レベル並列性を処理
クラスタコンピューティング
Beowulf クラスタ�1994年NASA T.Sterling
→現在のClusterの元祖となった
現在のClusterは、InfinibandなどのSANを使うものも多いが、基本的に上記の原則を守っている
Infiniband
| SDR | DDR | QDR |
1X | 2Gbit/s | 4Gbit/s | 8Gbit/s |
4X | 8Gbit/s | 16Gbit/s | 32Gbit/s |
12X | 24Gbit/s | 48Gbit/s | 96Gbit/s |
RHiNET-2 cluster
共有メモリ 対 メッセージパッシング
メッセージパッシングモデル
ブロッキング通信�(Blocking: ランデブ)
Send
Receive
Send
Receive
バッファ付き通信
Send
Receive
Send
Receive
ノンブロッキングのメッセージ通信
Send
Receive
Other Job
PVM (Parallel Virtual Machine)
MPI�(Message Passing Interface)
MPIのプログラミングモデル�
交信の種類
基本的なMPI関数
この6つが使えれば多くのプログラムが書ける!
その他のMPI 関数
例題1 Hello world
1: #include <stdio.h>
2: #include <mpi.h>
3:
4: #define MSIZE 64
5:
6: int main(int argc, char **argv)
7: {
8: char msg[MSIZE];
9: int pid, nprocs, i;
10: MPI_Status status;
11:
12: MPI_Init(&argc, &argv);
13: MPI_Comm_rank(MPI_COMM_WORLD, &pid);
14: MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
15:
16: if (pid == 0) {
17: for (i = 1; i < nprocs; i++) {
18: MPI_Recv(msg, MSIZE, MPI_CHAR, i, 0, MPI_COMM_WORLD, &status);
19: fputs(msg, stdout);
20: }
21: }
22: else {
23: sprintf(msg, "Hello, world! (from process #%d)\n", pid);
24: MPI_Send(msg, MSIZE, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
25: }
26:
27: MPI_Finalize();
28:
29: return 0;
30: }
初期化と終結
int MPI_Init(
int *argc, /* pointer to argc */
char ***argv /* pointer to argv */ );
argc と argvはコマンドラインからの引数.
int MPI_Finalize();
例
MPI_Init (&argc, &argv);
…
MPI_Finalize();
コミュニケータ制御用の関数�コミュニケータは通信用の空間��MPI_COMM_WORLDは、全プロセス用のコミュニケータ�ー>今回はこれを使う�
int MPI_Comm_rank(
MPI_Comm comm, /* communicator */
int *rank /* process ID (output) */ ); プロセスID(ランク)を返す
int MPI_Comm_size(
MPI_Comm comm, /* communicator */
int *size /* number of process (output) */ ); 全プロセス数を返す
例:
int pid, nproc;
MPI_Comm_rank(MPI_COMM_WORLD, &pid); 自分のプロセスID
MPI_Comm_rank(MPI_COMM_WORLD,&nproc); 全プロセス数
MPI_Send
1対1のメッセージ送信
int MPI_Send(
void *buf, /* send buffer */
int count, /* # of elements to send */
MPI_Datatype datatype, /* datatype of elements */
int dest, /* destination (receiver) process ID */
int tag, /* tag */
MPI_Comm comm /* communicator */ );
MPI_Send(msg, MSIZE, MPI_CHAR, 0,0, MPI_COMM_WORLD);
メッセージ用文字列配列msgの中の文字をMSIZE分プロセス0(タグも0)で送る
タグが一致したMPI_Recvでのみ受け取ることが可能
MPI_Recv
1対1のメッセージ受信
int MPI_Recv(
void *buf, /* receiver buffer */
int count, /* # of elements to receive */
MPI_Datatype datatype, /* datatype of elements */
int source, /* source (sender) process ID */
int tag, /* tag */
MPI_Comm comm, /* communicator */
MPI_Status /* status (output) */ );
char msg[MSIZE]
MPI_Status status;
MPI_Recv(msg, MSIZE, MPI_CHAR, 1, 0, MPI_COMM_WORLD, &status);
fputs(msg, stdout);
プロセス1からのタグ0で送って来たサイズMSIZEの文字列を受信し、msgに入れる。
MPI_Bcast
全プロセスに対してメッセージを転送
int MPI_Bcast(
void *buf, /* send buffer */
int count, /* # of elements to send */
MPI_Datatype datatype, /* datatype of elements */
int root, /* Root processor number */
MPI_Comm comm /* communicator */ );
if (pid ==0)
a=1.0;
MPI_Bcast(&a,1,MPI_DOUBLE, 0, MPI_COMM_WORLD);
pid 0が他の全てに対してa=1.0を転送する。
メッセージのデータタイプ
コンパイルと実行
wget http://www.am.ics.keio.ac.jp/arc/mpiex.tar
によりmpiexをダウンロード
tar xvf mpiex.tar
cd mpiex
% mpicc –o hello hello.c
% mpirun –np 4 ./hello
Hello, world! (from process #1)
Hello, world! (from process #2)
Hello, world! (from process #3)
4を色々変えてみよう!
コア数が4なので、これより大きくするとエラーで落ちる→OpenMPとの違い
例題2 reduct.c: 初期化
int pid, nproc, i;
FILE *fin;
double mat[N];
double sum, psum;
double start, startcomp, end;
MPI_Status status;
if((fin = fopen("mat4k.dat", "r"))==NULL) {
fprintf(stderr, "mat.dat is not existing\n");
exit(1);
}
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &pid);
MPI_Comm_size(MPI_COMM_WORLD, &nproc);
mat4k.datにデータが入っている
MPI Initialize
reduct.c: host (pid=0)
sum=0.0;
if (pid == 0) {
for (i = 0; i<N; i++) { fscanf(fin,"%lf", &mat[i]); }
start = MPI_Wtime();
for (i = 1; i < nproc; i++)
MPI_Send(&mat[i*N/nproc], N/nproc, MPI_DOUBLE, i, 0, MPI_COMM_WORLD);
startcomp = MPI_Wtime();
for(i = 0; i < N/nproc; i++) sum += mat[i];
for (i = 1; i < nproc; i++) {
MPI_Recv(&psum, 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &status);
sum += psum;
}
end = MPI_Wtime();
printf("%lf\n", sum);
printf("Total time = %lf Exect time= %lf [sec]\n", end-start, end-startcomp);
}
ファイルからデータ読み出し
MPI send
自分の分担の計算
配列要素の分散�
host pid=0
pid=1
pid=2
pid=3
reduction
reduction
reduction
reduction
0
N/nproc
2*N/nproc
3*N/nproc
reduct.c: pid≠0
else {
i=0;
MPI_Recv(&mat[i], N/nproc, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &status);
for(i = 0; i< N/nproc; i++) sum += mat[i];
MPI_Send(&sum, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
}
MPI_Finalize();
return 0;
}
データ受信
部分和
結果をホストに転送
実行してみよう
演習5: ssum.c
sum = 0.0;
for (i=0; i<N; i++)
for(j=0; j<N; j++)
sum += (x[i]-x[j])*(x[i]-x[j]);
ソースコードとコア数1,2,4の実行時間をhunga4125@gmail.comに提出
ヒント:データ分散の方針
host pid=0
pid=1
pid=2
pid=3
計算
計算
計算
計算
配列全体を転送して、自分の持ち分を計算
0
N/nproc
2*N/nproc
3*N/nproc