OpenMPI(v3.1.3)で複数ノードに渡る並列化ができなくて困っていた問題の解決法

OpenMPIの仕様由来かもしれないし、もしかするとtorqueとの相性かもしれないので、一応torqueもタグにつけてある。
# 問題の環境と症状
[CentOS7(研究室内新設サーバー)の設定メモ - Qiita](https://qiita.com/Yasushi-Shinohara/items/d38e80cc95c8a29bd1b2)に則ってネットワークをつないでアプリを入れた計算機群に[CentOSでのTorqueの設定 - Qiita](https://qiita.com/Yasushi-Shinohara/items/bd1218c79724f042d878)の手続きでTorqueを入れていた。
この自作クラスターマシンに

```console
$ ./configure --prefix=/opt/openmpi/3.1.3/intel-2019.3.199 FC=ifort F77=ifort CC=icc CXX=icpc
```
でconfigure, make, make installで、OpenMPIのv3.1.3を入れていた。
このMPIライブラリにリンクしたコンパイラーであるコードをビルドすると、ノード内ではMPIは問題なく動くが、ノード間並列で動かそうとすると、計算がスタックするという問題があった。具体的には

```console
[hoge002][[56811,1],7][btl_tcp_endpoint.c:626:mca_btl_tcp_endpoint_recv_connect_ack] received unexpected process identifier [[56811,1],0]
[hoge002][[56811,1],0][btl_tcp_endpoint.c:626:mca_btl_tcp_endpoint_recv_connect_ack] received unexpected process identifier [[56811,1],7]
[hoge002][[56811,1],3][btl_tcp_endpoint.c:626:mca_btl_tcp_endpoint_recv_connect_ack] received unexpected process identifier [[56811,1],5]
[hoge002][[56811,1],7][btl_tcp_endpoint.c:626:mca_btl_tcp_endpoint_recv_connect_ack] received unexpected process identifier [[56811,1],4]
[hoge002][[56811,1],4][btl_tcp_endpoint.c:626:mca_btl_tcp_endpoint_recv_connect_ack] received unexpected process identifier [[56811,1],10]
[hoge002][[56811,1],2][btl_tcp_endpoint.c:626:mca_btl_tcp_endpoint_recv_connect_ack] received unexpected process identifier [[56811,1],1]
[hoge002][[56811,1],4][btl_tcp_endpoint.c:626:mca_btl_tcp_endpoint_recv_connect_ack] received unexpected process identifier [[56811,1],3]
[hoge002][[56811,1],5][btl_tcp_endpoint.c:626:mca_btl_tcp_endpoint_recv_connect_ack] received unexpected process identifier [[56811,1],6]
[hoge002][[56811,1],6][btl_tcp_endpoint.c:626:mca_btl_tcp_endpoint_recv_connect_ack] received unexpected process identifier [[56811,1],2]
[hoge002][[56811,1],5][btl_tcp_endpoint.c:626:mca_btl_tcp_endpoint_recv_connect_ack] received unexpected process identifier [[56811,1],9]
[hoge002][[56811,1],6][btl_tcp_endpoint.c:626:mca_btl_tcp_endpoint_recv_connect_ack] received unexpected process identifier [[56811,1],11]
[hoge002][[56811,1],1][btl_tcp_endpoint.c:626:mca_btl_tcp_endpoint_recv_connect_ack] received unexpected process identifier [[56811,1],8]
```
という結果がエラー出力に書き込まれていて、計算が中途で止まっていた。また、標準出力からは計算が進んでいる様子が見えない一方、`top`でプロセスをみると100%で占有しているという状況。

もう少し問題を切り分けると、MPIからコードを呼び、`MPI_Init`, `MPI_Comm_size`, `MPI_Comm_rank`, `MPI_Get_processor_name`, `MPI_Finalize`の関数だけを使ったコードは、問題なく終了したことを確認している。この事実と上記のエラー出力から、MPIを使った通信が怪しいことが推察された。

# 基本的な解決法(この解決に引き続いて問題は現れたが、このセクションの事項とは独立なものだった)
参考URL群 [Time to remove the openib btl ? - 本当は怖いHPC](https://freak-da.hatenablog.com/entry/2018/04/20/122916), [--MCA flag does not activate btl framework to work with specified components (TCP/IB) · Issue #5808 · open-mpi/ompi](https://github.com/open-mpi/ompi/issues/5808), [TCP BTL: problems when there are multiple IP interfaces in the same subnet · Issue #5818 · open-mpi/ompi](https://github.com/open-mpi/ompi/issues/5818) を斜め読みすると、どうも複数のNICを持つ筐体でethernetでMPIの通信をするには、何かしら指定をしなければならないことがおぼろげながら推察される。そこで、実行時のオプションに` --mca btl_tcp_if_include eth0`を追加したところ、部分的に解決した。より完全なスクリプトは以下(適宜改変済み)

```bash:calc.sh
#!/bin/bash
#PBS -N hogehoge
#PBS -l nodes=2:ppn=12
#PBS -l walltime=72:00:00

cd $PBS_O_WORKDIR

LOG=./log.log

export OMP_NUM_THREADS=1
ulimit -s unlimited

MPIEXE=mpirun
EXE=a.out
IN=./input.inp
NMPI=24

${MPIEXE} --mca btl_tcp_if_include eth0 -np ${NMPI} ${EXE} < ${IN} >>${LOG}
```
ここで 部分的に と書いた意味は、どうも途中までは計算は普通に動いている兆候を見せるが、ある個所で必ず止まってしまった。これは次のセクションに記載するように、「根本的にノード間並列の計算が流れない」とは独立な問題である(可能性が著しく高い)ので、一応この問題はこの措置で解決したとみなす。

# セグフォの問題解決
上のセクションで触れた、計算が中途で止まった個所でみられる部分でのエラーは`forrtl: 致命的なエラー (174): SIGSEGV、segmentation fault occurred`で、ノードをまたがないように`#PBS -l nodes=1:ppn=24`にすれば現れないため、コードがをコンパイルして作られた実行ファイルで指示されている命令が本質的におかしいことは意味していないと推察される。また、ここで検証をしているコードは、他のスーパーコンピューターでは適切にビルドすることで、こうした問題には直面したことはないので、ビルドの仕方がまずいか、MPIのライブラリのビルドの仕方がまずいか、あるいはMPIの使い方がまずい という可能性が高い。

上で問題が現れた際に使っているアプリでは、多くのスタックサイズを使うもので、基本的に利用時に`ulimit -s unlimited`でスタックサイズを広げておかないと落ちてしまうものであることはほかの実行環境で確認済みであった。そこで、スタックサイズを見る前に、試しに先のスクリプトMPI実行部を

```bash:calc.sh(実行部分だけ差し替えた箇所)
${MPIEXE} -np ${NMPI} bash -c "hostname && ulimit -s"
```
に差し替えると、出力からスクリプトが読み込まれたノードではスタックサイズがunlimitedになっているが、MPIのスレーブとして呼ばれたホスト側ではスタックサイズが8192になっていることが分かった。したがって、計算の実行が進み、スタックサイズが8192を超えたタイミングで、スレーブ側のホストで立ち上がっているプロセスがセグフォで落ちることが予想される。
この予想が正しければ、基本的にMPIのスレーブ側でもスタックサイズがunlimitedになっていれば、動くはずである。[^1] 8192になっている理由はさておき、スタックサイズをunlimitedにしたうえで、実行できればよい。これを実現するためには

```bash:calc.sh(実行部分だけ差し替えた箇所)
${MPIEXE} -np ${NMPI} bash -c "ulimit -s unlimited && a.out"
```
に実行部分を差し替えることで実現できるはずで、事実これでセグフォは消えて、見ている範囲では正常に計算が実行された。というわけでめでたしめでたし。(bashの文法を使えば`a.out`の箇所をきれいな書き方にできるとは思うけれど、それはまたゆっくりと。)

[^1]: ここで使っているシステムは、特に`.bashrc`や`/etc/profile`等で指定はしていないものの、sshでログインした状態で`ulimit -s`で確認するとスタックサイズはunlimitedになっている。また、`ulimit -s`をbashのスクリプトに書いて、実行するとそこでもスタックサイズはunlimitedになる。しかし、MPIのスレーブだけ呼び出した時だけ、スタックサイズが8192という数字になっていて、妙ではある。`/etc/security/limits.conf`をみても初期値を8192にするような記述はないし、`/etc/security/limits.d/stack.conf`を見るとsoftもhardもunlimitedになっていて、ますます8192がどこから来ているのか謎である。

この理解が正しいとすると、「torqueのスクリプトの階層で入力したコマンドが、MPIのスレーブプロセスにすべては伝わっていないことが原因」で、これがtorqueからきているのか、OpenMPIからきているのかまではまだわかってない。
根本的には、これはそもそも実行ファイルが`ulimit -s unlimited`を要求するところも原因で、実行にあたって`--mca btl_tcp_if_include eth0`が必要だったのとは基本的に独立な問題と言ってよさそう。つまり、スタックサイズの要求が厳しくないアプリであれば、前者の解決策で十分だったに違いない。

参考URL:
https://www.t3.gsic.titech.ac.jp/node/443
http://disbauxes.upc.es/code/applied-physics-department/changing-the-stacksize-ulimit-s-limit-whenever-running-mpirun/
https://qiita.com/hdoi/items/3fcf6e57be47b679ec6c

## 考察(余談)
もし、Torqueの立ち上げ時にスタックサイズのデフォルト値が決まっているとすると、怪しい場所は`/etc/init.d/pbs_mom`。中を見ると

```:/etc/init.d/pbs_mom(抜粋)
ulimit -n 32768
```
という記述が見つかり、このファイルに`ulimit -s unlimited`を入れて、pbs_momデーモンを再度起動すれば、デフォルト値としてunlimiteのスタックサイズが設定できるのかもしれない。(本番環境での設定の変更は怖いので試していない。基本的にこのページでのすべての設定は一般ユーザー側での書き換えで済むものだった。)

### 参考情報
[How to set up stack-size to unlimited in PBS pro - Users/Site Administrators - OpenPBS](https://community.openpbs.org/t/how-to-set-up-stack-size-to-unlimited-in-pbs-pro/235)を見ると、OpenPBSの場合は`PBS_EXEC/lib/init.d/limits.pbs_mom`にulimitのデフォルト値が書かれているらしい。直上のファイルは、ここからの類推。