OpenMPI:プロセスとコアのバインド

はじめに

OpenMPとOpenMPIは間違いやすいよね!!

さて、私の開発している分子動力学コードGLIPSは、MPIとOpenMPのハイブリッド並列で計算が実行されます。 また、ここのメモで紹介している密度汎関数計算コードのOpenMXもMPIとOpenMPのハイブリッド並列化がされています。

ハイブリッド並列計算の実行は、例えばOpenMXであれば次の様にコマンドを打てばいいわけですね。

mpiexec -np 2 ./openmx C60.dat -nt 8
ここで、引数の-npでMPIによるプロセス並列数を設定しています。 引数の-ntはopenmxの独自オプションですがOpenMPによるスレッド並列数を設定しています。 これで、例えば16coreあるマシン上で実行すれば、2プロセス立ち上がり、それぞれのプロセスが8スレッド分のCPU負荷で回ります。 topコマンド上ではCPU使用率ほぼ800%のプロセスが2つ上がっているはずです。

いや、はずなんですよ!! ところが、OpenMPIのversion 1.8.1をインストールしたら、2プロセスは立ち上がるけど、それぞれのCPU使用率が100%までしか上がらない。OpenMPが無効なのかと思って調べると、そうでもなく、実行時のomp_get_num_threads()関数の返り値を表示するとちゃんと8と表示される。どうやら、8スレッドは立ち上がっているけど、それらの合計のCPU負荷が100%までに上限が設定されてしまっている模様。

というわけで、今回のメモは上記の問題の解決策です。 結局はOpenMPIの仕様変更でしたというお話。

プロセスとCPUコアのバインド

MPIでプロセス並列する人々にとっては、各プロセスは最初に担当したCPUコア上で常に回り続けて欲しい(プロセスとコアのバインド)ものです。 なぜなら、コアを移動してキャッシュもクリアされると、キャッシュ効率が上がらないとか、最近では、NUMA対応システムが多いから、 プロセスが別のコアへ移ると途端にメモリアクセス効率が落ちるとか、そういう問題が出てきます。 ですので、プロセスとコアのバインドはコード開発者にとっては至極もっともな要望です。

一方で、詳しくない人にとっては、「プロセスは常に同じコア上で回るんじゃないの?」と思われている人も多いと思います。しかし、実際は、プロセスは常にコア上を転々として移動しています。そういう、プロセスとコアのバインドされていないシステムが多いです。 理由としては、OSシステムとしてのロバスト性の問題だったり、ハードウェア的には複数CPUチップ上でプロセスを移り変わらせて各チップあたりの発熱具合を抑える、さらにはそれによってターボブーストを維持する、などなど理由があります。

実際にOpenMPI-1.6.5まではこの様なプロセスとコアのバインドはされていませんでした。 しかし、1.8.1ではデフォルトでプロセスとコアがバインドされる仕様に変わったようです。 公式サイトのフォーラムにもその告知と理由がつらつら書いてありました。

さて、MPIによるプロセス並列のみの人はこれでいいんですが、OpenMPによるスレッド並列も使ったハイブリッド仕様だとこれはこまる。 プロセスとコアがバインドというのは、要はそのプロセスは最初の担当コア上でしか回らないよ、ということなので、スレッドをいくつ立ち上げても別のコアを利用することができないのです。 その為に、最初の例は8スレッド立ち上げても、それらが全て一つのコア上に束縛されて、CPU利用率は100%までしかいかないのです。

よって、ハイブリッド並列計算の為にはプロセスとコアのバインドを外してやる必要があります。

バインド外し

ではプロセスとコアのバインドを外す方法を記します。 ただし、この方法はOpenMPI(しかも1.8.1しか保証しません)に限ったものなので、MPICHやIntelMPIでは必ずしも有効ではありません。

方法としては、job投入時のmpirunもしくはmpiexecの引数と"-bind-to none"を指定します。 最初の例でバインドを外すには次の様になります。

mpiexec -bind-to none -np 2 ./openmx C60.dat -nt 8
これだけです。

ここで指定した-bind-toの後にはnoneの他にcoreやsocktなどが指定できます。デフォルトがcoreを指定したもの相当になっているようです。

環境変数での指定ができればいいのですが、ちょっと見つけられなかったので、 私の環境ではログインシェルにてaliasを張りました。

#.cshrcなら
alias mpirun 'mpirun -bind-to none'
alias mpiexec 'mpiexec -bind-to none'

#.bashrcなら
alias mpirun='mpirun -bind-to none'
alias mpiexec='mpiexec -bind-to none'

余談

今回はハマって数時間もってかれました。 この手の仕様変更はもっと大々的に告知して欲しいです。 一緒にやっていたGlusterFSのバグとも格闘してたので合わせると2日間ちかい? GlusterFSではdistribute-replica = 3 x 2を張るとファイルがうまく作れないというバグがあったのですが、 最近のversion 3.5になって直ってくれたので大変嬉しい。

さらに余談(2015/02/16)

OpenMPIはversion1.8.1からlibmpi_f90やlibmpi_f77がなくなったようです。 代わりに、libmpi_mpifh と libmpi_usempif08を合わせて使うことで代替できます。 つまり、コンパイル時指定オプションの"-lmpi_f90"を" -lmpi_mpifh -lmpi_usempif08 "(両方指定)に変更せよとのことみたい。 こういう仕様変更は、もっと大々的に告知していただけるとありがたいのですが。。