Slurm Installその後4. mpi4pyでOpenMPI並列処理
はじめに
これまでの記事ではSlurmの
- アカウンティング設定
- リソース制限(Limit)-> acount/user associationsやQoSでの制限適用
をやってみました。
今回ですが、折角ジョブスケジューラを扱うので並列処理のSlurmでの設定も少し勉強してみたいと思いました。僕の環境の虚弱な計算ノード達ではありますが、並列処理の環境準備をやってみたいと思います。
目次
OpenMPI / mpi4py
並列処理の実行については、SlurmでいくつかのMPIがサポートされています。
MPI Users Guide slurm.schedmd.com
今回はそのうちの一つのOpenMPIを使うことなります。ただ、僕がC/C++/Fortranなどを通っていないので、(まだ親近感が多少ある)pythonを使って、そのバインディングである「mpi4py (MPI for Python)」で並列処理を動かしていきたいと思います。
ちなみにOpenMPIをPythonモジュールで扱えるものは今の所mpi4py一択のようです。今後困る事がなければ、マイLabではmpi4pyを使っていくつもりです。
必要なもの
環境はこれまでと同じで、以下がベースです。
・Slurm 22.05
・Master + Node1~4は全てRocky linux 8.6 (Virtual Box)
まず、今回の並列化環境に追加で必要なものを整理しておきます。
OpenMPI
MPI(Message Passing Interface)は、プロセス並列の為の標準的APIを提供するライブラリを指す。MPIの実装としてOSS利用可能なメジャーなものの一つがOpenMPI。
OpenMPI: https://www.open-mpi.org/
pmi
PMI(Process Management Interface)は元々はMPICHの一部として開発・配布されていたものっぽい。 MPI実装からプロセス管理機能が独立・標準化されたインターフェイスとして提供されるAPIで、リソースマネージャやジョブスケジューラから利用される事を想定している。
大まかなバージョンは
- PMI-1、PMI-2(PMI-1の改良番)、PMIx v1-v4 (xは「Exsascale」の意。またPMI-1,2と下位互換)。v5も開発が進んでいるそう。
(大規模な並列環境で)大量のMPIジョブプロセスを迅速にスケーラブルできる事を主眼に、プロセスの起動サポート、メモリ効率利用、各種MPI実装の分離開発、など可能にする。
参考:
deepdive into PMI(x) : https://container-in-hpc.org/artefacts/isc/2022/hpcw/pdf/5_hpc/3_PMIx-Deep-Dive.pdf
PMIx: Process Management for Exascale Environments : http:// https://dl.acm.org/doi/pdf/10.1145/3127024.3127027
mpi4py
MPI for Python — MPI for Python 3.1.3 documentation
設定1. 並列化
ではこれから実際に設定をやっていきます。
masterとNode1~4の全マシンに対して行います。
OpenMPI / PMI
mpi4pyを利用する為にもまずは、OpenMPIを入れておく必要があります。
またPMIですが、OpenMPIにもPMIはembededされていて、ビルド時に"--with-pmi"、"--with-pmix"オプションをつける事で「PMI」「PMIx」がビルドされるようです。
OpenMPIバージョンごとの対応は以下 Which Environments Include Support for PMIx? | OpenPMIx
ただ、SlurmのドキュメントをみるとSlurmインストール時点でPMIxは構築しておく必要があった(?)のか、後からPMIx追加をトライするも出来手こずりそうでした。なので今回はSlurmがデフォルト対応している「PMI-2」を使う事にしたい思います。
memo: 自前でPMIxをビルドする場合は以下やっておく
dnf -y install flex libev libevent-devel hwloc
では、現時点でのOpenMPI最新版を入れてみます。(公式 https://www.open-mpi.org/)
インストール先は/usr/local/openmpi
としておきます。
- cd /usr/local/src && sudo wget https://download.open-mpi.org/release/open-mpi/v4.1/openmpi-4.1.4.tar.gz
- cd /usr/local/src
- sudo tar -xvf openmpi-4.1.4.tar.gz && cd openmpi-4.1.4
- sudo ./configure --with-slurm --with-pmi --prefix="/usr/local/openmpi"
- sudo make && sudo make install
パス、ライブラリパス
PATHとLD_LIBRARY_PATHの設定が必要です。
今後、ユーザを適用に作って増やしたりすると思いますので/etc/profile.d
配下にopenmpi.sh
を作成してユーザ共通にしておきます。
sudo cat <<EOF > /etc/profile.d/openmpi.sh export PATH="\$PATH:/usr/local/openmpi/bin" export LD_LIBRARY_PATH="\$LD_LIBRARY_PATH:/usr/local/openmpi/lib" EOF
mpi4py
(なければ)先にpython3-develを入れておきます。
dnf -y install python3-devel
pipでインストールできます。
$ pip3 install mpi4py Collecting mpi4py Downloading https://files.pythonhosted.org/packages/20/50/d358fe2b56075163b75eca30c2faa6455c50b9978dd26f0fc4e3879b1062/mpi4py-3.1.3.tar.gz (2.5MB) 100% |████████████████████████████████| 2.5MB 389kB/s Installing collected packages: mpi4py Running setup.py install for mpi4py ... done Successfully installed mpi4py-3.1.3
→Ver3.1.3が入りました。ひとまずこれでOKとしましょう。
全マシン共通の設定は以上です。
設定2. NFS共有
NFS
続いてですが、折角の機会なのでNFS共有の仕組みを入れます。
(割愛)NFS共有の設定手順は割愛します。
※色んな方がWebに上げてくださっているので、検索して上位Hitしてくるやりかたで問題ないと思います。
僕の環境は/mnt/share
というディレクトリをマスタ(master.science.com)とNode1~4でNFS共有しました。
以下はnode1の例ですが、他のnode2~4でも同様に見える状態です。
[john@node1 ~]$ df -Th | grep nfs master.science.com:/mnt/share nfs4 70G 6.3G 64G 9% /mnt/share
サンプル並列処理
mpi4pyをインポートして標準出力を行う、だけのサンプルです。
NFS共有ディレクトリを利用して/mnt/share/[user]/mpitest.py
と配置しておきます。
from mpi4py import MPI comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() name = MPI.Get_processor_name() print(f"Hello, world! from rank {rank} out of {size} on {name}")
補足
- MPIプログラムで生成される各プロセスは一意な値が設定され、
get_rank()
メソッドで取得できる - コミュニケータ(通信を実施するためのプロセスのグループ)のサイズは
get_size()
メソッドで取得できる - 生成されるプロセスの名前が
Get_processor_name()
メソッドで取得できる
設定3. slurm.conf
/etc/slurm/slurm.conf
以下のようにデフォルトを変更しておきます。 noneのままでもコマンドラインのオプションで--mpi=pmi2と毎回入力して実行もできます。
#MpiDefault=none MpiDefault=pmi2
.confを変更したので以下も実施しておきます。
/etc/slurm/slurm.conf
をnode1~4に配布してslurmdを再起動masterもslurmctldを再起動
設定はこれで以上です。
では、サンプルの並列処理を動かしていきたいと思います。
動かしてみる
では動かして行きます。前回から引き続きjohnユーザを使います。
今まではずっとsbatchでしたが、今回はインタラクティブに動かしてみようと思います。
- salloc
- srun
salloc
Slurmのマシンリソースの割当を要求します。
johnユーザでnode1から実行します。ノードへの計算負荷は全くない状況です。
確認しておくと/etc/slurm/slurm.conf
は以下の設定です。
PartitionName=partition1 Nodes=node[1-2] Default=YES MaxTime=INFINITE State=UP PartitionName=partition2 Nodes=node[3-4] Default=YES MaxTime=INFINITE State=UP
リソースは--nodes=2で2ノードの割当を要求してみます。
[john@node1 ~]$ salloc --nodes=2 bash salloc: Granted job allocation 376 [john@node1 ~]$
→Granted job allocation 376と、無事リソースが割当てられたようです。 (--ptyをオプションにつけると割当されたノードのリモートホスト上で、対話側シェルが起動された状態になります)
この段階でsqueueを確認してみます。
[john@master john]$ squeue JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON) 376 partition interact john R 0:11 2 node[3-4]
→Node3、4が割り当てられている事がわかります。NAMEは指定しなければ"interact"と表示されるようです。
それではsrunを実行してみます。
※/mnt/share
NFS共有ディレクトリで実行しているので、mpitest.pyを各Nodeへ配布する必要はありません。
するとsrun python3 /mnt/share/john/mpitest.pyを実行した所で、
ImportError: libmpi.so.12: cannot open shared object file: No such file or directory srun: error: node3: tasks 0-1: Exited with exit code 1
libmpi.so.12がないと言われます。mpi4pyでは「libmpi.so.12」としてOpenMPIのライブラリが参照されるようで、
- cd /usr/local/openmpi/lib
- sudo ln -s libmpi.so.40.30.4 libmpi.so.12
でopenmpi4.1.4のライブラリを参照できるようにしました。
(ちなみにOpenMPIを1.10.2のバージョンでやると上記エラーは発生しませんでした)
それでは気を取り直してsrunを再度行います。
[john@node1 ~]$ srun python3 /mnt/share/john/mpitest.py Hello, world! from rank 0 out of 2 on node3 Hello, world! from rank 1 out of 2 on node4
→2ノード(Node3,4)で並列がそれぞれ実行されました。コミュニケーター内のプロセス総数(=2)で、各プロセスが一意に割当られていそう(rank=0,1)ですね。上手く動いていそうです。
ひとまずexit
します。
[john@node1 ~]$ exit exit salloc: Relinquishing job allocation 376 salloc: Job allocation 376 has been revoked. [john@node1 ~]$
exit
したタイミングでsqueueをたたくと、割当解除が確認できます。
[john@master john]$ squeue JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
試しに先ほどより1台ノードを欲張って、--nodes=3で3ノードを要求してみると...
[john@node1 ~]$ salloc --nodes=3 bash salloc: Requested partition configuration not available now salloc: Pending job allocation 377 salloc: job 377 queued and waiting for resources
[john@master john]$ squeue JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON) 377 partition interact john PD 0:00 3 (PartitionNodeLimit)
→リソース割当可能なPartitionが存在しない場合は、PartitionNodeLimit
で保留(PD)されてしまうようです。ctrl+zでキャンセルしておきます。
念の為、partitionの設定を変えて挙動の変化があるか?見てみます。
4ノードを全て同じpartition(partition_all)と変更してみました。
[slurm@master ~]$ sinfo PARTITION AVAIL TIMELIMIT NODES STATE NODELIST partition_all* up infinite 4 idle node[1-4]
sallocで--nodes=4を要求してみると、
[john@node1 ~]$ salloc --nodes=4 bash salloc: Granted job allocation 408
→partitionを変更してノードを増やしているので(2→4),、ちゃんと--nodes=4で割当てされましたね。さきほどは2を超えるとpartitionのリソースを超えて保留されました。
squeueでも割当が確認できます(node[1-4])
[john@master john]$ squeue JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON) 408 partition interact john R 0:12 4 node[1-4]
では、srunでmpitest.pyを実行します。
[john@node1 ~]$ srun python3 /mnt/share/john/mpitest.py Hello, world! from rank 0 out of 4 on node1 Hello, world! from rank 2 out of 4 on node3 Hello, world! from rank 3 out of 4 on node4 Hello, world! from rank 1 out of 4 on node2
→4ノード(Node1~4)に対して、4並列で問題なく動いていそうです。
今回は以上になります。
最後に
今回は、
- mpi4run(OpenMPI)を使った並列処理の設定
- 動作確認(salloc, srun)、partition設定を変えてみる
をやってみました。
今回は手動でsallocなどをやりましたが実運用だと、ノードの数や種類、ジョブのワークロードも様々です。そのあたりをSlurmがいい感じにスケジューリングしてくれると考えると、少しワクワクしてきますね。