ChainerMNをMPIで実行中に、例外でプロセスが死んでも実行が止まらない問題
通常、MPIプログラムは、実行中のどれかのrankがエラー等で以上終了した場合(あるいはMPI_Finalize
を呼び出さずに終了した場合)は全プロセスが強制終了されることが期待されます。
が、ChainerMNを含む mpi4py
を用いたプログラムを実行している場合、Pythonの例外によってプロセスの1つが異常終了しても、その他のプロセスが終了せずにハングしてしまうという問題があります。
# test.py def func(): import mpi4py.MPI mpi_comm = mpi4py.MPI.COMM_WORLD if mpi_comm.rank == 0: raise ValueError('failure!') mpi4py.MPI.COMM_WORLD.Barrier() if __name__ == '__main__': func()
実行結果:
$ mpiexec -n 2 python test.py Traceback (most recent call last): File "main.py", line 27, in <module> func() File "main.py", line 21, in func raise ValueError('failure!') ValueError: failure! # <----- このまま固まっていて、プロセスが終了しない
この時、オンプレミスのクラスタ環境等であれば、logを監視したりなどして手動で kill
すればよいのですが、クラウド環境やジョブスケジューラ下ではなかなかそうもいきません。特にクラウド環境では、計算が進んでいないのに課金だけが進んでしまうという状態になるので非常にまずいですね。
この場合、Pythonの例外処理機構をフックして、処理されていない例外が発生した場合に MPI_Abort()
を呼び出すことによってMPIプロセスを強制終了することができます。
import sys # Global error handler def global_except_hook(exctype, value, traceback): import sys from traceback import print_exception print_exception(exctype, value, traceback) sys.stderr.flush() import mpi4py.MPI mpi4py.MPI.COMM_WORLD.Abort(1) sys.excepthook = global_except_hook def func(): import mpi4py.MPI mpi_comm = mpi4py.MPI.COMM_WORLD if mpi_comm.rank == 0: raise ValueError('failure!') mpi4py.MPI.COMM_WORLD.Barrier() if __name__ == '__main__': func()
実行結果:
$ mpiexec -n 2 python main.py Traceback (most recent call last): File "main.py", line 26, in <module> func() File "main.py", line 20, in func raise ValueError('failure!') ValueError: failure! -------------------------------------------------------------------------- MPI_ABORT was invoked on rank 0 in communicator MPI_COMM_WORLD with errorcode 1. NOTE: invoking MPI_ABORT causes Open MPI to kill all MPI processes. You may or may not see output from other processes, depending on exactly when Open MPI kills them. --------------------------------------------------------------------------
Time to remove the openib btl ?
Open MPI devel メーリングリストで、「openib
コンポーネントを削除する時が来たのでは?」という議論が始まっています。
Open MPIでInfinibandを使う場合、ながらく openibib
という BTL
コンポーネントが使われてきました。
BTL
というのは、Byte Transfer Layer
というOpen MPIのコンポーネント階層です。例えば、環境テスト等の目的でInfinibandの使用を強制したい場合、次のように mpiexec
を起動すれば可能です(IBが使えなければ、TCPにフォールバックすることなくエラーとなる)。
$ mpiexec --mca btl openib ${PROGRAM}
(mca
は、Module Component Architectureの略で、Open MPIのモジュールシステムの名前)
しかし、この ib
コンポーネントは古くてバグが多いのが問題点でした。InfinibandのAPIであるibverbsは、仕様が曖昧で使いづらいところも多く、次世代のAPIへの以降が望まれている状態です。
現在、高性能インターコネクトの標準APIとして、(このブログでつらつらと書いているように)UCX
が期待されています。Infinibandハードウェアの主要ベンダーであるMellanoxも、ibverbsではなくUCXをサポートするというのが方針のようです。
すぐに移行するには、まだiWARP
などのハードのサポートが十分ではないという意見もあるようですが、これは完全に時間の問題だと思われます。
UCXを試す(7)
勉強がてら作成中の UCXのC++ラッパーをGithubに上げました。たぶんライブラリ関数の網羅率は5%くらいだしUCXの使い方をほとんどわかっていないので、あえてREADMEも何も書いていません。少しずつ機能を試して実装しながらライブラリとしての形を整えていきたいと思います。
ところで、下記のコマンドを使って上記のリポジトリに含まれているプログラム hello_world
を実行すると、以下のようなエラー(警告?)が表示されます。原因と対策を調査中。ソース読むしかないかなー。
$ cd ucxcpp $ cmake .. [~/ucxcpp/build] -- The C compiler identification is GNU 4.8.4 -- The CXX compiler identification is GNU 4.8.4 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Found MPI_C: /home/kfukuda/mpi/openmpi-2.1.2/lib/libmpi.so -- Found MPI_CXX: /home/kfukuda/mpi/openmpi-2.1.2/lib/libmpi.so -- Found Git: /usr/bin/git (found version "1.9.1") -- Configuring done -- Generating done -- Build files have been written to: /home/xxxxxxx/ucxcpp/build $ make ... [100%] Built target hello_world $ mpiexec -n 2 -mca btl tcp,self -host host1,host2 ./hello_world rc / mlx4_0:1 [1520851183.876675] [sakura171:12401:0] sys.c:555 UCX ERROR A new segment was to be created and size < SHMMIN or size > SHMMAX, or no new segment was to be created. A segment with given key existed, but size is greater than the size of that segment. Please check shared memory limits by 'ipcs -l'. ================================= List of resources: self / self / self tcp / tcp / ib0 tcp / tcp / eth0 tcp / tcp / eth1 ib / rc / mlx4_0:1 ib / ud / mlx4_0:1 cuda_cp / cuda_copy / cudacopy0 sysv / mm / sysv posix / mm / posix cma / cma / cma ================================= rc / mlx4_0:1 [1520851183.886542] [sakura173:16109:0] sys.c:555 UCX ERROR A new segment was to be created and size < SHMMIN or size > SHMMAX, or no new segment was to be created. A segment with given key existed, but size is greater than the size of that segment. Please check shared memory limits by 'ipcs -l'. sent string = IGUVOYG Rank 0 Done. Received: IGUVOYG Rank 1 Done.
MPIを使っているのは、デバイスのアドレス交換(ランデブー)のためです。UCXに含まれているオリジナルのhello_world
だと、ソケットを使っているのですが、それだとクライアントとサーバーの両方のプロセスを立ち上げるのが面倒なため、起動メカニズムとしてOpen MPIを使っています。MPIは通信には -mca btl tcp,self
を指定しているので、TCPのみが使われます。
共有メモリ(memory mapped file)関係のエラーぽいですが、よくわからん。(エラーと言われているが動くし)