これは多分MPI Advent Calendar 2017の12日目の記事です。
はじめに
わりと過疎ってる感のあるMPI Advent Calendar 2017ですが、10日目にMPIプログラムのデバッグという記事が投稿されました。それを見て、「そういう奴作った!」と思ったので紹介します。
MPIと標準出力
MPIを使っていると、標準入出力まわりは結構面倒です。馬鹿パラとかやってる時、出力順序はどうなってもいいから複数プロセスから適当に標準出力に吐きたい、ということがたまにあります。この時、
printf("My rank is %d\n",rank);
とかやると、
My rank is 0
My rank is 3
My rank is 1
My rank is 2
などとなって問題ないのですが、
std::cout << "My rank is " << rank << std::endl;
とかやると
My rank is My rank is 0
3
My rank is 1My rank is
2
などとなったりします。これはprintf
では一つのプロセスが書き終わるまで他のプロセスが待っているのにたいして、std::cout
は、<<
のタイミングで別のプロセスが割り込む可能性があるからです。
で、先の記事にあったようにOpenMPIの--output-filename
オプションとか使うと便利なのですが、このオプションのないMPI処理系もあるし、各プロセスからの出力をファイルに吐きつつ、たまに標準出力にも吐きたい、なんてことがあるので、それ用のストリームを自作しました。
MPIStream
というわけでできたのがこれです。
動作原理は簡単で、std::cout
の代わりにmout
に突っ込むと、内部でstd::stringstream
にためておいて、std::endl
が来たらランクに対応したファイルに出力しているだけです。
使い方はこんな感じです。
#include <iostream>
#include <mpi.h>
#include "mpistream.hpp"
int
main(int argc, char **argv){
MPI_Init(&argc, &argv);
int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
mout.SetRank(rank,size);
mout << "My rank is " << rank << std::endl;
MPI_Finalize();
}
-
mpistream.hpp
をインクルードする。 -
mout.SetRank
に、自分のランクとトータルのプロセス数を渡す。 - 後は
std::cout
の代わりにmout
に吐くと、rXXXX.out
というファイルに出力される
実行するとこんな感じになります。
$ mpirun --oversubscribe -np 4 ./a.out
$ cat r*.out
My rank is 0
My rank is 1
My rank is 2
My rank is 3
ただし、トータルプロセス数が1の時には、mout
に出力された内容はstd::cout
にも出力されます(r0000.out
にも出力されます)。
$ ./a.out
My rank is 0
デバッグ時にシングルプロセスで実行した時にmout
がstd::cout
の代わりになるのでいろいろ便利です。
まとめ
MPIプログラムで、std::cout
と似たような感じで使えるストリームクラスMPIStream
を紹介しました。自分のプログラムで使っていますが、地味に便利なので、もしみなさんもよかったら使ってやってください。