MPIでhelloを作成してみる.
helloを出力する簡単なサンプルをとおしてMPIプログラミングを開始する.
あんまりグダグダ言っても面白くないので,何はともあれサンプルを示してみる.
このプログラムは
cc hello.c -lmpiとか
mpicc hello.cでコンパイルできる.後者はlamやmpichをインストールすると使用できるコマンドで,MPI特有の処理を施しつつccを呼び出す.このプログラムは
mpirun -np 8 a.outなどとして実行できる.上記は-npでプロセス数を8としている.実行結果を以下に示す.
$ mpirun -np 4 a.out Hello, my process rank is 1 Greetings from process 2! Greetings from process 3!次節以降にサンプルの解説を記す.
サンプルのMPI特有の部分は"MPI_"で始まる識別子を含む文である.MPI識別子には命名規則がある.MPIの識別子は"MPI_"で始まる.MPI定数はMPI_CHARなどのように"MPI_"の後に大文字が続く.MPI関数はMPI_Initなどのように"MPI_"の後に最初に大文字一文字,その後に小文字が続く.
全てのMPI関数は
#include "mpi.h" int MPI_Init(int *argc, char ***argv)を一回だけ実行した後で呼び出す(※).MPI_Initにはmain関数の引数のポインタを渡す.これによってMPI_Initは起動時のコマンドラインオプションを取得し,MPIのための環境を準備する.実はMPI自体ではコマンドライン引数を指定していないのだが,MPI実装では使用できるオプションのいくつかを以下に示す.
- -mpiqueue
- MPI_Finalizeが呼び出されたときに,メッセージキューの状態を出力する.デバッグ用である.
- -mpiversion
- MPI実装のバージョン(MPIのバージョンではない)を出力する.この値はconfigureスクリプトを実行したときに取り込まれた値である.
- -mpinice nn
- nice値を値nn分増やす.値nnはroot以外は正でなければならない.全てのシステムがこの引数をサポートしているわけではないが,ignoreはしない.
- -mpimem(MPICH用)
- もしMPICHが-DMPIR_DEBUG_MEMを指定してビルドsれていれば,全てのmallocとfreeをチェックして,メモリ空間に損害を与える兆候がないか見る.
#include "mpi.h" int MPI_Finalize()を実行する.MPI_Initを実行する前に他のMPI関数を呼び出すことはできないし,MPI_Finalizeを実行したあとに他のMPI関数を呼び出すこともできない.
※こうしたMPI関数ヘッダのドキュメントはネット上にいくつかある.また,
apt-get install mpi-docすると
/usr/share/doc/mpi-doc/www/index.htmlからも参照できる.
MPIプログラムの各実行プロセスにはそれぞれランクという値が設定される.SPMD(※)であるMPIプログラムはこのランク毎に処理を分岐させる.プロセスのランクは以下で取得する.
#include "mpi.h" int MPI_Comm_rank ( MPI_Comm comm, int *rank )commはコミュニケータという通信集団,すなわち通信しあえるプロセスの集合を指定する.コミュニケータについては次節でもう少し述べる.commはMPI_Comm型の定数を指定する.この定数には以下がある.
- MPI_COMM_WORLD
- すべてのプロセス
- MPI_COMM_SELF
- その定数を呼び出しているプロセス
※Single Program Multi Data
MPI_Sendはメッセージを送信する関数である.
#include "mpi.h"
int MPI_Send( void *buf, int count, MPI_Datatype datatype, int dest,
int tag, MPI_Comm comm )
MPI_Sendのパラメータは全て入力パラメータである.各パラメータの説明を以下に示す.
- buf
- 送信バッファの先頭アドレス.
- count
- 送信バッファの要素数.
- datatype
- 送信バッファの要素の型.
- dest
- 宛先プロセスのランク.
- tag
- メッセージのタグ.
- comm
- コミュニケータ.
#include "mpi.h"
int MPI_Recv( void *buf, int count, MPI_Datatype datatype, int source,
int tag, MPI_Comm comm, MPI_Status *status )
MPI_Recvの出力パラメータについて以下に示す.
- buf
- 受信バッファの先頭アドレス
- status
- 受信結果のステータス.
- count
- 受信バッファのサイズ.これは実際に受信するメッセージよりも大きければ,同じでなくても構わない.足りなければ溢れる.
- datatype
- 受信バッファの要素の型.
- source
- 送信元プロセスのランク.
- tag
- メッセージのタグ.
- comm
- コミュニケータ
※タグはメッセージタイプとも呼ばれる.
MPIデータ型には,C,Fortranそれぞれに対応したものがある.以下にCに対応するMPIデータ型を示す.
以下はMPI関数MPI_MAXLOCとMPI_MINLOC(謎)用である.
以下はC,Fortranの両方で使用できるMPI型である.
| MPIデータ型 | Cの型 |
|---|---|
| MPI_CHAR | char |
| MPI_BYTE | unsigned charと同様 |
| MPI_SHORT | short |
| MPI_INT | int |
| MPI_LONG | long |
| MPI_FLOAT | float |
| MPI_DOUBLE | double |
| MPI_UNSIGNED_CHAR | unsigned char |
| MPI_UNSIGNED_SHORT | unsigned short |
| MPI_UNSIGNED | unsigned int |
| MPI_UNSIGNED_LONG | unsigned long |
| MPI_LONG_DOUBLE | long double(いくつかのシステムでは実装されていないかもしれない) |
| MPI_FLOAT_INT | struct{float, int} |
| MPI_LONG_INT | struct{long, int} |
| MPI_DOUBLE_INT | struct{double, int} |
| MPI_SHORT_INT | struct{short, int} |
| MPI_2INT | struct{int, int} |
| MPI_LONG_DOUBLE_INT | struct{long double, int}(これはオプションで,多分NULLにセットされる) |
| MPI_LONG_LONG_INT | struct{long long, int}(これはオプションで,多分NULLにセットされる) |
| MPI_PACKED | MPI関数MPI_PackとMPI_Unpack用 |
| MPI_UB | 関数MPI_Type_struct用.upper-boundの場合に使用(?) |
| MPI_LB | 関数MPI_Type_struct用.lower-boundの場合に使用(?) |
サンプルのランク0のプロセスでは,自分以外の全プロセスからメッセージを受信してそれを出力する.すなわちプロセスの総数を知る必要がある場合がある(総数 - 1回MPI_Recvする必要がある).プロセス数の取得には以下を使用する.
#include "mpi.h" int MPI_Comm_size ( MPI_Comm comm, int *size )入力パラメータにcommはコミュニケータである.プロセス数は第二引数sizeに格納される.
いきなり細部について書きすぎた感があるが,細部は後で必要に応じて参照することにすれば,MPIプログラミングの概要は掴めたと思う.MPI_Initしてメッセージの送受信を堪能してMPI_Finalize,これが基本である.タグとコミュニケータの応用については,別の記事でも特に取り上げたいと思う.

