はじめに
MPIプログラムをgdbでデバッグするためのまともな方法が見つからず、あまりにしんどかったのでmpitx
というツールを作った、というお話です。
デモ動画を見てもらえると分かりやすいかもしれないですが、各MPIプロセス毎に端末が割り当てられてtmuxで制御できます。
tmuxの機能としてキーボード入力を全ての端末に複製して送ることができるので、gdbによるデバッグ等に便利です。
さらに、複数ノードにも対応させたので、スパコンやクラスタ等の環境でも動作します。
これは某スパコンで実際に実行したときのキャプチャですが、
hostname
の結果から別のノードでの実行をインタラクティブに制御できているのが分かると思います。
必要になった背景
MPIプログラムをgdbでデバッグするには、下の記事でも言及されているようにあんなことやこんなことをしてやる必要がありました。
いちいちデバッグのためにコンパイルし直すのもなんだかなあという気がして、もっと簡単にgdbを起動できる何かが欲しいなあと思っていたところ、tmpi
なるスクリプトを見つけました。
これがmpitx
の原型で、大体の機能は既にあってありがたく使わせていただいていました。
しかし、しばらく使っているうちにいくつか不満が出てきたので作り直したという次第です。
mpitx
と tmpi
の比較:
-
mpitx
はマルチノード対応、tmpi
はマルチノード不可 -
mpitx
はMPI実装固有のオプションをそのままmpiexec
に渡せる、tmpi
ではそれが面倒 -
mpitx
はpython3スクリプト、tmpi
はbashスクリプト- 細かい話:
tmpi
ではreptyr
コマンドがなければMPIの子プロセスに環境変数を引き継ぐことができないが、mpitx
ではpython3のいい感じに上手いことやっているのでreptyr
への依存なしで環境を引き継げる
- 細かい話:
色々あるんですが、大きかったのはtmpi
がマルチノードに対応していなかったことでした。
複数ノードでしか再現しないバグがたまーに出現するので、この機能はぜひとも欲しいところ。
使い方
だいたいはGitHubの方に書いたので、そちらを参照してもらえればと思います。
単なるpython3スクリプトなので、python3とtmuxがあればうごくはず、です。
簡単に試したければこれでインストール:
pip3 install git+https://github.com/s417-lama/mpitx.git
使い方は
mpitx [OPTIONS]... -- [COMMANDS]...
こんな感じで、[OPTIONS]...
の部分がmpiexec
のオプションとしてそのまま渡されます。
コマンドとの区切りの--
は必須になっていて、--
のあとの[COMMANDS]...
がtmuxの各端末上で実行され(ているように見え)ます。
--
が必須なのはオプション解析をサボるためで、MPI実装固有のオプションが渡されてもそれをそのままmpiexec
に投げられるようにするためです。
一方tmpi
では独自のオプション体系を持っていたので、mpiexec
にそのまま渡したいパラメタがあると大変です(--hostfile
とか諸々)。
例えば4プロセスでbashを起動したければ
mpitx -n 4 -- bash
gdbでデバッグしたければ
mpitx -- gdb --args ./a.out arg1 arg2 ...
こんな感じで簡単にできます。
実装について
細かい話ですが簡単にメモしておきます。
単に使う上では知らなくても困らないと思うので適宜読み飛ばしてください。
mpitx
コマンドで立ち上げられた大元のプロセスをP
としましょう。
まず渡されたオプションはmpiexec
にそのまま渡し、コマンドはmpitx
のラッパを挟んでmpiexec
に渡しておきます。
そしてmpiexec
によって立ち上げられたMPIの子プロセスをC_i
(i
はMPIランク)とします。
次に、tmuxの各端末に標準入出力をプロキシするプロセスを立ち上げておきます。
MPIプロセスの数だけ端末を用意するので、これらのプロセスを同様にT_i
と表記します。
ここで、P
とT_i
は同一のノードで実行されていますが、これらとC_i
が同一のノードで実行されている保証はありません。
これらのプロセス間での情報のやり取りにはTCPソケットを用いています。
まず最初にP <---> C_i
間の接続を確立し、次にP <---> T_i
間の接続を確立します。
T_i
ではリバースシェルと同様の原理で、標準入出力をC_i
へフォワードするためにC_i
からの接続を待ち受けます。
T_i
のホスト名およびポート番号はP
を介してC_i
に伝えられ、C_i
からT_i
への接続が完了します。
この時点でC_i
がmpitx
に渡されたコマンドを実行し、T_i
の端末からC_i
のセッションをインタラクティブに操作することが可能になります。
TCPソケットを用いた実装である以上、マルチノードでmpitx
を使うにはノード間がイーサネットで接続されていてソケット通信が通ることが条件になります。
接続できるポートが制限されていたりすると動かないかもしれないです。
最後に
gdbやtmuxなど、使い慣れたツールを気軽に使えた方がデバッグへの心理的障壁が下がるのでおすすめです。
MPI向けにはTotalViewのようなリッチなデバッガもあるようですが、オープンソースではないし使えるシステムも限られてくるので...。
主にデバッグ用途で紹介していましたが、マルチノードで各計算機の状態を確認する用途でもこれは結構便利です。