はじめに
これはMPI Advent Calendar 2017の18日目の記事です。
MPI Advent Calendar 2017 はMPIプログラムのデバッグ技法の記事が多い気がしますが、MPIプロファイリングインタフェースを使う方法を書いたものがないので、この「MPIプログラムのデバッグ方法〜MPIプロファイリングインタフェース編〜」を書きます。
MPIプロファイリングインタフェースというものがある
MPIにはMPIプロファイリングインタフェースというものが1.0のときから規格として存在します。
どういうものかをざっくり書くと、
- 各MPI関数は
MPI_
を接頭詞とせず、PMPI_
を接頭詞としても呼び出し可能。機能や引数は接頭詞MPI_
のものと全く同じ...と言うか 単なる別名 。- 例えば、
MPI_Send
をPMPI_Send
で呼べる。
- 例えば、
- 接頭詞
MPI_
のMPI関数は ユーザプログラムで再定義できる 。
というもの。
パチェコの本が手元にあるなら、とりあえず12章6.1節を読むと良いでしょう。
具体的使い方としてはこんな感じで
#include <mpi.h>
int MPI_Send(void* buffer, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
{
int ret;
/* ここで何か固有処理(1) */
ret = PMPI_Send(buffer, cont, datatype, dest, tag, comm);
/* ここで何か固有処理(2) */
return ret;
}
とすることで、ユーザプログラム定義のMPI_Send関数が作れます。
呼び出し側は特に何もする必要はありません。
MPI_Send(...);
などと通常のMPI関数と同様にコールすればOKです。
固有処理を通信ごとに分けたいような場合は、tagで制御するといいです。
なお、各MPIライブラリはこれをどう実現しているのかはよくわかりません。調べてもいませんが。特にFortranではどうやってるんでしょうね。
MPIプロファイリングインタフェースを使ったデバッグ
さて、MPIプロファイリングインタフェース(以下、"PMPI関数"と呼ぶ)はその名前のとおり、タイマ関数を使うなどして通信性能(正確にはMPI関数の経過時間)を測定することを目的としています。たぶん。
そしてこれはデバッグにも使うことができます。
たとえば、MPIプログラムをデバッグするときは、通信内容をダンプしたいことがよくあります。少なくとも私はある。
プログラム中のMPI関数の呼び出しの前後にダンプするコードを挿れいていくのは大変手間です。そんなとき、ダンプの対象としたいMPI関数を、PMPI関数のラッパとしてユーザプログラムで再定義してラッパ内でダンプ処理することで容易に実現できます。
え?そんなことしたら、全部の通信がダンプされて大変なことになるって?tagで処理を分けましょう。異なる通信は異なるtagを着けるのが作法ですよ。(自戒)
デバッグ事例
こういう記事を書くということは、わたしの経験としてPMPI関数がデバッグに強烈に役立った経験があるからなわけで、その例を簡単に紹介。(実際のコードは手元に残ってないので擬似コードで)
バグの事象としては、プログラムを実行すると毎回ほぼ同じタイミングでMPIの内部エラーでプログラムが終了を起こすというもの。そういうわけで、まずはそのプログラムで使っている全てのMPI関数をPMPI関数を使って再定義して、呼び出し回数をカウントするようにしました。
こんな感じ
static unsigned int mpi_send_count = 0;
int MPI_Send(void* buffer, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
{
int ret;
mpi_send_count++;
printf("MPI_Send: %d\n", mpi_send_count);
ret = PMPI_Send(buffer, cont, datatype, dest, tag, comm);
return ret;
}
この結果、毎回同じ呼び出しカウントでコケることが判明。続いて、
static unsigned int mpi_send_count = 0;
int MPI_Send(void* buffer, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
{
int ret;
mpi_send_count++;
if (mpi_send_count == コケるカウント) {
/* 引数とスタックトレースをダンプする処理 */
}
ret = PMPI_Send(buffer, cont, datatype, dest, tag, comm);
return ret;
}
として、問題が発生するMPI関数の呼び出し位置とタイミングを特定して、デバッグの材料とした・・・ということがありました。まあ、結果としてMPIライブラリの実装のバグだったのですけども。(おこ)
まとめ
- MPIプロファイリングインタフェースというのがあります。
- 使い方しだいでデバッグにも使えます。