SCoreクラスタによる並列プログラミング

SCore クラスタによる並列プログラミング
天野研究室 PDARCH グループ
1
大塚 智宏
SCore とは
SCore は,新情報処理開発機構で開発された,PC/WS クラスタ用の並列プログラム開発・実行環境である.異なるマシンや
ネットワークの混在するクラスタ上でも動作し,ユーザはクラスタ全体を単一のシステムのように扱うことができる.また,
メッセージパッシング,分散共有メモリ,マルチスレッド並列といった複数の並列プログラミングパラダイムをサポートする.
SCore は,図のように,ユーザレベルオペレーティングシステム SCore-D と,並列プログラム開発環境である SCASH,MPICHSCore,MPC++,およびこれらソフトウェアの下位層に位置する低レベル通信ライブラリ PM II 等から構成される.
User Applications
SCASH
MPICH
-SCore
MPC++
User
Level
SCore-D
PM II
Kernel
Level
Device drivers
SCore-D プロセッサやネットワークといったクラスタのリソースを管理するユーザレベルのグローバルオペレーティングシ
ステムである.SCore-D により,ユーザは SMP クラスタや異種混在クラスタも単一のシステムであるかのように扱うことがで
きる.また,クラスタ上でのマルチユーザ環境を提供し,並列ジョブの効率的なスケジューリングを行う.
SCASH PM II を用いたソフトウェア DSM (Distributed Shared Memory) システム.update と invalidate の両方のプロトコルを
用いた LRC (Lazy Release Consistency) モデルを採用している.
MPICH-SCore MPICH をベースとした MPI ライブラリ.プロセス間通信に PM II を用いている.
MPC++ C++のテンプレート機能を用いた,マルチスレッドテンプレートライブラリ.同期/非同期リモート関数の呼び出し
機構,同期構造体,グローバルポインタなど様々な機能を提供する.SCore-D は MPC++で記述されている.
PM II クラスタコンピューティング用の低レベル通信ライブラリで,さまざまな種類のネットワークや共有メモリに同一の
方法でアクセスできるように慎重に設計されている.現在のところ,Myrinet,Ethernet,Shmem (SMP ノード内の共有メモリ)
用の PMII ドライバが実装されている.
2
ICS SCore クラスタの構成
•
•
•
•
•
•
3
Supermicro SuperServer 6010H × 65 (サーバノード 1,計算ノード 64.ただし 3 台は故障中)
プロセッサ : Dual Pentium III 933MHz
メモリ : 約 900Mbytes
OS : RedHat Linux 7.2 (kernel 2.4.18)
ネットワーク : 100Mbps Fast Ethernet,Switching Hub で結合
SCore 5.0.1
MPI を用いた並列プログラミング
MPI (Message Passing Interface) は,分散メモリ型並列計算機上でメッセージパッシングモデルに基づく並列プログラミング
を行うための標準的な API 仕様である.現在では,メッセージパッシングの事実上の標準として広く用いられている.
1
3.1
メッセージパッシング
分散メモリ型並列計算機 (クラスタも含む) では,各プロセスは独立なアドレス空間を持つため,共有変数による並列プログ
ラミングは行えない.各プロセスは,メッセージを交換し合うとにより,協調して処理を進める.
メッセージ通信には,point-to-point 通信と collective 通信の 2 種類がある.point-to-point 通信では,一対のプロセス間で,片
方が送信者 (sender),もう片方が受信者 (receiver) として,それぞれ送受信のための関数を実行して通信を行う.一方,collective
通信では,2 つ以上のプロセスが同じ関数を実行して協調的な通信を行う.
また,MPI では,SPMD (Single Program Multiple Data) 型と呼ばれる形式に基づいてプログラミングを行う.これは,複数の
プロセスが全て同一のプログラムを実行し,プロセス番号によって分岐して別々の処理を行うもので,並列プログラミングで
は最も一般的なプログラム記述法である.MPI 環境下でプログラムが実行されると,指定した数のプロセスが生成され,並列
計算機やクラスタ内の各プロセッサに分配される.
3.2
サンプルプログラム
MPI では,最低限 6 つの関数を使えばたいていのプログラムを記述することができる.以下に,サンプルプログラムを示す.
¶
³
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:
µ
•
•
•
•
•
•
•
•
•
#include <stdio.h>
#include <mpi.h>
#define MSIZE
64
int main(int argc, char **argv)
{
int pid, nproc, i;
char msg[MSIZE];
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &pid);
MPI_Comm_size(MPI_COMM_WORLD, &nproc);
if (pid == 0) {
for (i = 1; i < nproc; i++) {
MPI_Recv(msg, MSIZE, MPI_CHAR, i, 0, MPI_COMM_WORLD, &status);
fputs(msg, stdout);
}
}
else {
sprintf(msg, "Hello, world! (from process #%d)\n", pid);
MPI_Send(msg, MSIZE, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
}
MPI_Finalize();
return 0;
}
´
2: ヘッダファイル mpi.h をインクルードする.
12: MPI の初期化.
13: 自プロセスのプロセス番号を求める.
14: 全プロセス数を求める.
16∼21: プロセス 0 の場合の処理.
17∼20: 自分以外の全プロセス (プロセス 1 ∼ nproc−1) から順にメッセージを受信し,表示する.
22∼25: プロセス 0 以外の場合の処理.
24: プロセス 0 にメッセージを送信する.
27: MPI の終了.
2
3.3
基本的な 6 つの関数
MPI Init
MPI の初期化を行う.argc, argv には,コマンドライン引数として受け取ったものをそのまま渡さなければならない.
¶
³
int MPI_Init(
int
char
µ
´
*argc,
***argv
/* argc へのポインタ */
/* argv へのポインタ */ );
MPI Finalize
MPI の終了処理を行う.
¶
³
int MPI_Finalize();
µ
´
MPI Comm rank
コミュニケータ comm 中の自プロセスのランク (プロセス番号) を求める.
¶
³
int MPI_Comm_rank(
MPI_Comm
int
µ
´
comm,
*rank
/* コミュニケータ */
/* プロセス番号が返される領域 */ );
MPI Comm size
コミュニケータ comm 中のプロセス数を求める.
¶
int MPI_Comm_size(
MPI_Comm
int
µ
comm,
*size
³
/* コミュニケータ */
/* プロセス数が返される領域 */ );
´
MPI Send
プロセス dest にデータを送信する.
¶
int MPI_Send(
void
int
MPI_Datatype
int
int
MPI_Comm
µ
*buf,
count,
datatype,
dest,
tag,
comm
³
/*
/*
/*
/*
/*
/*
送信バッファ */
送信データ配列の要素数 */
送信データ配列の MPI データ型 */
送信先プロセス番号 */
タグ */
コミュニケータ */ );
´
MPI Recv
プロセス source からデータを受信する.
¶
int MPI_Recv(
void
int
MPI_Datatype
int
int
MPI_Comm
MPI_Status
µ
*buf,
count,
datatype,
source,
tag,
comm,
*status
³
/*
/*
/*
/*
/*
/*
/*
受信バッファ */
受信データ配列の要素数 */
受信データ配列の MPI データ型 */
送信元プロセス番号 */
タグ */
コミュニケータ */
受信ステータスが返される領域 */ );
´
3
• コミュニケータ ( MPI_Comm 型) は,プロセスのサブセットを「通信空間」として扱うためのもので,全プロセスで構成さ
れる MPI_COMM_WORLD があらかじめ定義されている.とりあえず,引数 comm には MPI_COMM_WORLD を指定すればよい.
• タグは,メッセージを区別するためのもので,送信側と同じタグを指定しないとメッセージは受信できない.
• MPI_Recv の引数 status には,MPI_Status 型変数へのポインタを渡す.MPI_Status 型は,MPI_SOURCE, MPI_TAG,
MPI_ERROR の 3 つのメンバを持つ構造体で,それぞれ送信プロセス ID,タグ,エラーコードが格納される.
• MPI_Send および MPI_Recv では,パラメータ count と datatype でメッセージの大きさを指定する.すなわち,メッセー
ジは count 個の値の列を含み,それぞれの MPI データ型が datatype である.MPI データ型には以下のものがある.
MPI データ型
MPI_CHAR
MPI_SHORT
MPI_INT
MPI_LONG
MPI_UNSIGNED_CHAR
MPI_UNSIGNED_SHORT
MPI_UNSIGNED
MPI_UNSIGNED_LONG
MPI_FLOAT
MPI_DOUBLE
MPI_LONG_DOUBLE
3.4
対応する C データ型
char
short
int
long
unsigned char
unsigned short
unsigned int
unsigned long
float
double
long double
collective 通信
collective 通信では,2 つ以上のプロセスが同一の関数を実行して協調的な通信を行う.collvective 通信関数の機能は基本的
にはすべて MPI_Send や MPI_Recv 等の point-to-point 通信関数の組み合せで代用できる.しかし,効率的に実装されたもので
あれば,point-to-point 通信を用いて手でチューニングするよりは楽にパフォーマンス向上が得られる場合があるし,プログラ
ムが読みやすくなるというメリットがある.
ここでは主な関数の紹介に留め,それぞれの詳細には触れないが,興味のある方は後述の資料を参照して頂きたい.
MPI Bcast
プロセス root から全プロセスに同じデータを送信 (ブロードキャスト) する.
¶
³
int MPI_Bcast(void *buf, int count, MPI_Datatype datatype, int root, MPI_Comm comm);
µ
´
MPI Scatter
プロセス root の送信バッファ中のデータを全プロセスに分配 (スキャッタ) する.
¶
³
int MPI_Scatter(void *sendbuf, int sendcount, MPI_Datatype sendtype,
void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm);
µ
´
MPI Gather
全プロセスに分散するデータをプロセス root の受信バッファにまとめる (ギャザ).
¶
³
int MPI_Gather(void *sendbuf, int sendcount, MPI_Datatype sendtype,
void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm);
µ
´
MPI Allgather
全プロセスに分散するデータを全プロセスの受信バッファにまとめる (オールギャザ).
¶
³
int MPI_Allgather(void *sendbuf, int sendcount, MPI_Datatype sendtype,
void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm);
µ
´
4
MPI Alltoall
各プロセスが全プロセスへデータをスキャッタする.または,各プロセスが全プロセスからデータをギャザする (全対全ス
キャッタ/ギャザ).プロセス i の j 番目のブロックは,プロセス j の i 番目のブロックに格納される.
¶
³
int MPI_Alltoall(void *sendbuf, int sendcount, MPI_Datatype sendtype,
void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm);
µ
´
MPI Reduce
全プロセスのデータに対してリダクション演算 (最大値や和など) を実行し,結果をプロセス root の受信バッファに格納する.
¶
³
int MPI_Reduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype,
MPI_Op op, int root, MPI_Comm comm);
µ
´
MPI Allreduce
全プロセスのデータに対してリダクション演算を実行し,結果を全プロセスの受信バッファに格納する.
¶
³
int MPI_Allreduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype,
MPI_Op op, MPI_Comm comm);
µ
´
3.5
その他
MPI Get processor name
プロセスが実行されているホスト名を取得する.
¶
³
int MPI_Get_processor_name(char *hostname, int *length);
µ
´
MPI Barrier
バリア同期を行う.すなわち,全プロセスがこの関数を呼ぶまでプロセスをブロックする.
¶
³
int MPI_Barrier(MPI_Comm comm);
µ
´
MPI Wtime
ある時点からの経過時間 (掛け時計時間) を返す.単位は秒.
¶
³
double MPI_Wtime();
µ
´
以下に,実行時間の測定例を示す.
¶
³
double start, end;
...
MPI_Barrier(MPI_COMM_WORLD);
start = MPI_Wtime();
/* 全プロセスがここにたどり着くまで待つ */
/* 測定開始 */
...
/* 何らかの処理 */
end = MPI_Wtime();
if (pid == 0)
printf("Exec time = %f [sec]\n", end - start);
µ
5
/* 測定終了 */
/* 表示 */
´
MPI にはこの他にも非常に多くの関数がある.中でも重要なのはノンブロッキング通信関数である.ノンブロッキング通信
において,送信や受信は,要求を発行する関数と,終了を待つ関数とに分かれている.要求の発行と終了待ちとの間に他の演
算を行うことで,通信と計算のオーバラップを行い,パフォーマンスを向上させることができる.詳しい説明は省略するが,興
味のある方は後で紹介する資料を参照されたい.
4
MPICH-SCore の使い方
MPICH-SCore を使うためには,まず SCore のサーバノードにログインする必要がある.ICS のクラスタでは,サーバノード
は rhinetfs.ics.keio.ac.jp (131.113.126.140) である.ssh でしかログインできないので注意.
¶
³
localhost% ssh -l ログイン名 rhinetfs.ics.keio.ac.jp
µ
´
さきほどのサンプルプログラムをコンパイルし,実行してみよう.コンパイルコマンドは mpicc である.mpicc は内部で C
コンパイラとして gcc を用いており,オプションには gcc のオプションをそのまま使用できる.
¶
³
rhinetfs% mpicc -O2 -Wall -o hello hello.c
µ
うまくコンパイルできたら,実行してみよう.実行するためには,mpirun というコマンドを用いる.
¶
´
³
rhinetfs% mpirun -np 4x2 hello
SCore-D 5.0.1 connected (jid=167).
<0:0> SCORE: 8 nodes (4x2) ready.
Hello, world! (from process #1)
Hello, world! (from process #2)
Hello, world! (from process #3)
Hello, world! (from process #4)
Hello, world! (from process #5)
Hello, world! (from process #6)
Hello, world! (from process #7)
µ
´
-np オプションによって,起動するプロセス数を指定する.SCore の mpirun では,-np 4x2 のように指定することで,4 つ
の SMP ノードでそれぞれ 2 プロセスずつ,計 8 プロセス起動する,ということが可能である.ICS のクラスタは各ノードが
デュアルプロセッサなので,1 ノードにつき 2 プロセスまで起動できる.同じ計 8 プロセスでも,-np 8x1 とすれば,8 つの
ノードで 1 プロセスずつ,となる.
実行結果の初めの 2 行は SCore-D からのメッセージで,SCore-D のバージョンや起動したプロセス数などが表示される.ま
た,ここでは hello コマンドに引数はないが,通常のコマンドと同様にコマンドライン引数を与えることもできる.
5
課題
MPI を用いて,正方行列の積を並列に計算するプログラムを作成せよ.
• プロセス数は 64 まで.(ただし,3 台ほど故障しているので,-np 64x1 という指定はできない.)
• 行列のサイズは 1024 × 1024, 2048 × 2048, 4096 × 4096 (できれば),各要素は double 型とする.
• 行列の初期データはある 1 つのプロセスで生成し,計算結果もそのプロセスに集める.実行時間には,データを配った
り,結果を集めたりするための通信に要した時間も含むものとする.
• 結果の検証もきちんと行うこと.ただし,実行時間に含める必要はない.
• ここで紹介した以外の MPI 関数を用いても構わない.
すべてのソースファイルをホームディレクトリ以下のどこかのディレクトリにまとめ,
• ディレクトリのパス,ソースファイルのリスト
• プログラムの解説
• 考察・感想
を書いたメールを 7 月 19 日 24:00 までに [email protected] 宛に提出する.Subject は [SCore] ログイン名 とする
こと.正しく提出されれば,自動的にリプライメールが送信される.
なお,コピーしたと見なせるレポートがあった場合,それらのレポートは採点しない.誰かにコピーされたりしないように
パミッションを設定するなど,自分のファイルは自分で保護すること.
質問があれば,[email protected] まで.
6
6
アカウントについて
配布した用紙に必要事項を記入し,提出する.ログイン名はこちらで決定し,本日中にメールで連絡する.なお,課題提出
期限を過ぎた後,アカウントは削除するので,必要なファイルはバックアップを取っておくこと.
• 簡単なパスワードを付けない.
• 簡単なソースファイル編集,コンパイル,課題に関連した計算処理以外の目的で利用しない.
• プロセッサ,メモリ等のリソースを浪費しない.長時間放置されているプロセスを発見したら,こちらで処理するので
メール等で知らせて欲しい.
• ファイルの消失等に関しては一切責任を負えない.各自でバックアップを取るように.
混雑している時にプログラムを実行すると,リソースを取り合うため思うように実行時間が計測できなくなる恐れがある.
提出期限が近くなると混雑することが考えられるので,早めに取り掛かることをお勧めする.
7
情報源など
この授業のページ
SCore の開発・配布元
MPICH の開発・配布元
MPI の本家
MPI の資料
RHiNET の開発元 (参考)
天野研 PDARCH グループ (参考)
A
http://www.am.ics.keio.ac.jp/comparc/
http://www.pccluster.org/
http://www-unix.mcs.anl.gov/mpi/mpich/
http://www.mpi-forum.org/
http://www.ppc.nec.co.jp/mpi-j/
http://pdarwww.rwcp.or.jp/
http://www.am.ics.keio.ac.jp/proj/pdarch/
参考:乱数の発生
¶
³
#include <stdlib.h>
#include <time.h>
#define N
1024
void make_table()
{
double *a;
int i;
a = malloc(N*sizeof(double));
srand48(time(NULL));
for (i = 0; i < N; i++)
a[i] = 2.0*drand48() - 1.0;
}
µ
´
7