MPI超超入門 第2回 「もっとHello world編」

前回はお約束通り「Hello world」してみましたが、実行時オプションで強制的に並列実行しただに過ぎませんでした。
そこで今回は、きちんとした「MPIでのHello world」をしてみたいと思います。

まず最初に、MPIを使用したもっとも簡単なプログラムについて紹介します。

最低限のMPIプログラム

program main
include 'mpif.h'
call MPI_INIT(IERR)
call MPI_COMM_SIZE(MPI_COMM_WORLD,NPROCS,IERR)
call MPI_COMM_RANK(MPI_COMM_WORLD,MYRANK,IERR)

write(*,*)'Hello world!','NPROCS=',NPROCS,'MYRANK=',MYRANK

call MPI_FINALIZE(IERR)
stop
end

このソースコードについて説明します。

include 'mpif.h'

インクルードファイル'mpif.h'がインストールされる場所は環境によって異なります。MPIを使用したプログラムを書く場合は必ずこのmpif.hをインクルードしてください。この中身について知る必要はありません。*1

call MPI_INIT(IERR)

MPI_INITで指定する引数(この例ではIERR)は必ず指定される必要があります。コールしたMPIサブルーチンが正常に終了すれば「0」、エラーが発生した場合はそれ以外の値が戻ってきます。
このサブルーチンと指定する引数を指定しなかった場合segmentation fault等でエラーとなるので、必ず指定される必要があります。

call MPI_COMM_SIZE(MPI_COMM_WORLD,NPROCS,IERR)
call MPI_COMM_RANK(MPI_COMM_WORLD,MYRANK,IERR)

ここではまとめて説明します。この二つのサブルーチンはコミュニケータに属しています。
MPI_COMM_SIZEの第二引数(ここではNPROCS)は、MPI実行時のプロセス数です。
MPI_COMM_RANKの第二引数(ここではMYRANK)は、MPI実行時の各プロセスに割り振られる背番号です。
二つのサブルーチンの第一引数「MPI_COMM_WORLD」はコミュニケータと呼ばれるグループ名です。
コミュニケータは、MPI実行時の全てのプロセスをまとめたものです。

call MPI_FINALIZE(IERR)

MPIの終了処理を行うサブルーチンです。これを書かなかった場合、誤作動を引き起こすことがありますので必ず記述してください。

コンパイルと実行の例1(某工業大学スパコンの場合)

mpif90 hello_mpi.f -o hello_mpi.out -mcmodel=medium
mpirun -n 4 hello_mpi.out
Hello world! NPROCS= 4 MYRANK= 2
Hello world! NPROCS= 4 MYRANK= 1
Hello world! NPROCS= 4 MYRANK= 0
Hello world! NPROCS= 4 MYRANK= 3

全てのプロセスで「Hello world! NPROCS= 4」が表示された一方で、「MYRANK」の数がプロセスごとに異なりました。
これは、プロセスの総数を表すNPROCSが一定である一方、MYRANKの値がそれぞれのプロセスに固有であることからご理解いただけるご思います。
また、注目すべき事柄として「MYRANK」が「1」から始まる数ではなく、「0」から始まる数であること、さらに重要なことに「MYRANK」が昇順あるいは降順になるとは限らないことが挙げられます。
特に後者に関してよくある誤解として「MYRANKの順番どおりに処理が実行される」というものがありますがこれは間違いですので、プログラムを書くときは「どのプロセスが一番先に始まり、どのプロセスが一番最後に終わるかは実行時によって違う」ということを覚えておいてください。実際、この次に実行した際の結果は以下のようなものでした。

Hello world! NPROCS= 4 MYRANK= 0
Hello world! NPROCS= 4 MYRANK= 3
Hello world! NPROCS= 4 MYRANK= 1
Hello world! NPROCS= 4 MYRANK= 2

次に、同様のプログラムをC言語で書いた場合について説明します。

#include "mpi.h"
#include
int main(int argc, char **argv)
{
int n, myid, numprocs, i;

MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
printf("Hello world! numprocs=%d myid=%d\n", numprocs, myid);
MPI_Finalize();
}

このソースコードについて説明します。

#include "mpi.h"

C言語の場合はインクルードファイル'mpi.h'を必ずインクルードしてください。この中身について知る必要はありません。

MPI_Init(&argc,&argv);

MPI_Initで指定する引数はmain関数で指定される引数と同様にする必要があります。FORTRANのIERRに相当する引数は指定する必要はありません。

MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);

MPI_Comm_sizeの第二引数(ここではnumprocs)は、MPI実行時のプロセス数です。
MPI_Comm_rankの第二引数(ここではmyid)は、MPI実行時の各プロセスに割り振られる背番号です。
二つのサブルーチンの第一引数「MPI_COMM_WORLD」はコミュニケータと呼ばれるグループ名です。
FORTRANのように第三引数IERRに相当する引数は必要ありません。

MPI_Finalize();

MPIの終了処理を行うサブルーチンです。これを書かなかった場合、誤作動を引き起こすことがありますので必ず記述してください。
引数を指定する必要はありません。

コンパイルと実行の例1(某工業大学スパコンの場合)

mpicc hello_mpi.c -o hello_mpi.exe -mcmodel=medium
mpirun -n 4 hello_mpi.exe
Hello world! numprocs=4 myid=3
Hello world! numprocs=4 myid=2
Hello world! numprocs=4 myid=1
Hello world! numprocs=4 myid=0

次回からはデータ並列処理のプログラムを紹介します。

*1:中身についての簡単な説明はまたの機会にしたいと思います