C++
MPI

MPI用のstd::coutのようなもの

More than 1 year has passed since last update.

これは多分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

というわけでできたのがこれです。

https://github.com/kaityo256/mpistream

動作原理は簡単で、std::coutの代わりにmoutに突っ込むと、内部でstd::stringstreamにためておいて、std::endlが来たらランクに対応したファイルに出力しているだけです。

使い方はこんな感じです。

main.cpp
#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

デバッグ時にシングルプロセスで実行した時にmoutstd::coutの代わりになるのでいろいろ便利です。

まとめ

MPIプログラムで、std::coutと似たような感じで使えるストリームクラスMPIStreamを紹介しました。自分のプログラムで使っていますが、地味に便利なので、もしみなさんもよかったら使ってやってください。