♾️

InfiniBand QDR を Ubuntu 20.04 or 22.04 で設定するメモ(2023 年版)

2023/07/10に公開

情報

https://tatuiyo.xyz/?p=456

https://nekodaemon.com/2022/08/31/Tips-of-configuring-InfiniBand-Adapters/

https://qiita.com/tetrar124/items/2e4b7e5955401dbff0fb

https://wiki.archlinux.jp/index.php/InfiniBand

執筆時点(2023/07)ですと ConnectX-3 が中古でよく出回っている感じですね.

ConnectX-3 VPI ですと Eth mode もありますが, 10 GbE どまりになります.

RoCE(Ethernet(TCP/IP)互換で RMDA 的なのを行う) だと 40 GbE 相当になるのかしら? 著者が IB をよくいじっていたころ(2014 年くらい)は RoCE はまだまだ未成熟だった記憶がありますが, 最近はあたりまえのように使えるのかしらん.

いずれにせよ, ConnectX-3 は OEM 品多かったりいろいろ RoCE の過渡期の HW (RoCE v2 は ConnectX-3 Pro 以上)な感じなのようなので, 今から(2023/07 以降) IB を調達しようと考えている方は, ConnectX-4 以上を選んだほうがよいでしょう.
(ConnectX-4 だと IB のは 1.5 万円ほど? IB ではない 25GbE 品が安くある(1 万円くらい)ようですが...)

環境

注意点

Ubuntu のバージョン違いだと, software stack のバージョン違いで動かないものがります.
たとえば MPI や, ib_read_bw など. https://bugzilla.redhat.com/show_bug.cgi?id=1883219

Ubuntu のバージョンの統一を強くおすすめします(2023/07 時点では 20.04 がよいでしょう(おおむね, 現 LTS のひとつ前の LTS バージョンを使う)

  • Ubuntu 22.04 + ConnectX QDR(40 Gbps)(第一世代!) MT_0BB0110003
    • およそ 9 年位前に調達して, 最近ずっと眠っていた
    • fw ver: 2.8.60
  • Ubuntu 20.04 + ConnectX-3 QDR(40 Gbps) MT_1090110028
    • これはおよそ 7 年前くらいに調達.
    • fw ver: 2.42.5000

ファームウェアアップデートですが, (最新の) ファームウェアアップデートツール mstflint は ConnectX, ConnectX-2 には対応しておりませんでしたので, ConnectX は調達時点のまま(2.8.600)を使いました.
mst の古いバージョン(3.0 あたり?)であればファームウェアアップデートツール動くかもですが...

ConnectX-3 は mst 4.24 でアップデートできました.

OFED?

https://www.openfabrics.org/ofed-for-linux/

かつて InfiniBand 関連(HPC 用インターコネクトや, Fiber channel など)で, OSS で開発されていたソフトウェアスタックです. もうメンテはされていないようです.
ドライバやカーネル関連は Linux kernel に直に取り込まれたり, ソフトウェアスタック周りは libfabric https://ofiwg.github.io/libfabric/ で主にメンテされるようになっています.

OFED の多くは Unbutu 標準パッケージに登録されているので, 最新の InfiniBand HW 使うとかでなければ, apt 以外でインストールするものはありません.
(libfabric も apt で入ります)

たとえば ConnectX-3(mlx4), ConnectX-4 あたりであれば Ubuntu 標準ので事足ります.

Windows の場合はベンダ(現在は NVIDIA)が WinOF(OFED の Windows 版をベンダが改修したりしているやつ)として配布しています. https://network.nvidia.com/products/adapter-software/ethernet/windows/winof-2/

Switch いりますか?

とりあえず直接接続でもいけます. 2 port ある HBA(ネットワークカード)が多いので頑張れば 4~8 台くらいであればリング接続 or チェーン接続でもいけるはず... ですが, 4 台以上つなぎたいときはスイッチ買うとよいでしょう.

IS5022(8port. QSFP QDR(40 Gbps))が 1 ~ 2 万円くらいで eBay などで手に入ります.
ファンがうるさいのでファン交換必須(or ファンに抵抗いれておそくする)!

http://nittofc2.blog.fc2.com/blog-entry-37.html

https://qiita.com/syoyo/items/43e125d46bfecc7ba770

https://www.youtube.com/watch?v=VY-z-themss

ただ, switch のファン電源はサーバ用のためかピン配置が普通の PC 用と異なり, そのまま置き換えしてもファンが回りません. ファン用には別途で USB などから給電するとよいでしょう.

OpenSM

subnet manager は, 高いスイッチには HW で搭載されているようですが, HBA(ネットワークカード自体) or 安いスイッチだと搭載されていないので, Software でネットワークを管理するサービス(OpenSM)をどこかの PC で一つ立てておく必要あります.

HW 確認

ibv_devinfo, ibv_devices あたりで確認できます.

接続確認

ケーブル接続してリンクしていると ibnodes で相手先がとりあえず見えます.

ibwarn: [16002] mad_rpc_open_port: can't open UMAD port ((null):0)

と出たら sudo ibnodes しましょう.
(umad の sys ファイルの permission をユーザー OK にするのがいいかも)

ネットワーク設定

IPoIB

InfiniBand 上に TCP/IP 流して Ethernet っぽく見せる.
一部の InfiniBand サービスで相手先を見つけるのに IPoIB が必要になったりします
(MPI とか? IPoIB で相手先見つけて, その後 RDMA 接続)

手動で設定が必要です.
DHCP off で固定 IP でよいでしょう.

Ubuntu 20.04 から(18.04 から?), ネットワークインターフェイス名が CentOS っぽくなりました.

$ ip a

$ sudo lshw -c network

で確認しておきます.

ibp7s0 (2 ポートある場合は, 2 ポート目は ibp7s0d1) のような名前になっているはずです.

ネットワーク設定は, Ubuntu 20.04 からは, netplan 経由でないと設定できないようですが,

https://qiita.com/zen3/items/757f96cbe522a9ad397d

実際のところ netplan のバグで infiniband 関連は設定できませんでした...
(ネットにある設定ためしたがダメだった)

$ cat /etc/netplan/01-network-manager-all.yaml 
# Let NetworkManager manage all devices on this system
network:
  version: 2
  renderer: NetworkManager

Ubuntu デフォルトでは NetworkManager 経由になっていると思いますので, NetworkManager で設定します.

https://web.chaperone.jp/w/index.php?ib/IPoIB

IPv6 は disabled がよいでしょう.

著者環境だと, nmtui でのデフォルト設定では ipv6 enabled なので, Datagram mode では不安定で通信していると ip が無効になってしまい, IPv6 に切り替わってしまうみたいな動作になっていました.

dmesg を出すと

IPv6: ADDRCONF(NETDEV_CHANGE): ibp12s0: link becomes ready

というメッセージが多量に出たり...

あと, 192.168 ですでに既存ネットワークがある場合はこれも不安定になるようでした(パケットの干渉? or fw 関連? 要確認)

10.0 や 172.16 など IB 専用の IP 範囲を使うとよいでしょう.
最終的に nmtui での設定では以下のようになりました.

/etc/NetworkManager/system-connections/ に ib 設定が記録されますのでそちらも参照ください.

再度 ip a して, IP が割り振られているのを確認しておきます!

3: ibp12s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 2044 qdisc fq_codel state UP group default qlen 256
    link/infiniband 
    xxxxxxxx
    inet 10.0.100.100/24 brd 10.0.100.255 scope global noprefixroute ibp12s0
       valid_lft forever preferred_lft forever
    inet6 xxxxxxxx link noprefixroute 
       valid_lft forever preferred_lft forever

通常の TCP/IP でもそうかもしれませんが, 適切に IP 振っておけば IPoIB でも probe パケットが飛んで行って相手を見つけるので, ゲートウェイ IP 設定などは不要です(たとえば 192.168.250.100/24, 192.168.250.101/24 と設定すれば, 192.168.250.1 などの IP を割り当てたノードを用意しなくともよい)

開通確認

あとは普通に ping なりするだけです.

...
64 bytes from 10.0.100.100: icmp_seq=3 ttl=64 time=0.221 ms
64 bytes from 10.0.100.100: icmp_seq=4 ttl=64 time=0.189 ms
64 bytes from 10.0.100.100: icmp_seq=5 ttl=64 time=0.202 ms
64 bytes from 10.0.100.100: icmp_seq=6 ttl=64 time=0.226 ms
64 bytes from 10.0.100.100: icmp_seq=7 ttl=64 time=0.186 ms
64 bytes from 10.0.100.100: icmp_seq=8 ttl=64 time=0.183 ms
64 bytes from 10.0.100.100: icmp_seq=9 ttl=64 time=0.182 ms
...

IB 自体はレイテンシは数 ns です.
ip のオーバヘッドを考えると 0.2 ms くらいは妥当なところでしょうか.
ConnectX-3 同士だとレイテンシはより改善されるかもしれません.

パフォーマンス計測

iperf3

まずは iperf3 で計測してみます.

$ iperf3 -s # server
# client
$ iperf3 -c 10.0.100.100 
...
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  6.07 GBytes  5.21 Gbits/sec  9806             sender
[  5]   0.00-10.04  sec  6.06 GBytes  5.19 Gbits/sec                  receiver

5 GbE 相当です. まあスレッド化なりしたら理論値(32 Gbits/sec)に近くなるかもしれません.
また, IPoIB で Connection mode だと 15 Gbps くらい出ました. IPoIB でもそこそこ性能欲しい場合は Connection mode にしておくとよいかもです.

qperf

https://qiita.com/syoyo/items/1516f62404fd0011768e

Ubuntu だとデフォだとポートは解放されています(ufw disabled)

# server
$ qperf
# client
$ qperf 10.0.100.100 rc_rdma_read_bw
rc_rdma_read_bw:
    bw  =  3.47 GB/sec

Voila! おおむね QDR(8/10 encoding) の理論値(3.2 GB)が出ました!

トラブルシューティング

  • PCI-ex スロットにきちんとささっているか
  • ケーブルはかちっとなるまで挿さっているか

とくに銅線のケーブルは太くて重く, ケーブル抜き差し時に HBA が PCIex スロットからはずれてしまいやすいです(PC ケースに固定が理想だが, ミニクラスタを構築したい場合オープンエアにすることが多く固定が難しいときがある)

opensm のデーモンが active (exited) になる.

不明. Ubuntu 22.04 で発生する.

とりあえず opensm 動かすのは Ubuntu 20.04 がよいでしょう.

アプリ

ユーザから見た主なアプリは

  • MPI
  • pytorch + NCCL/RCCL
  • 既存の TCP/IP(socket)
  • 自前 RDMA アプリ(uverbs とかつかったり!)

あたりかなと思います.

MPI

以下は両ノードとも Ubuntu 20.04 で行いました.
Ubuntu 20.04 + 22.04 だと動かないのでご注意ください
(ソースから openmpi 関連ビルドしたとしても, openmpi が依存する ibverbs や librdma とかあたりでバージョン違うからうまくいかない)

openmpi, mpich どちらも apt パッケージでデフォで rdma 用の backend が入っています.
昔は mpich のほうが InfiniBand 対応がよかった記憶がありますが, 最近ですと openmpi も InfiniBand 対応したのと, 全体的に openmpi のほうが主流そうですので, openmpi がよいでしょうか.
(ただ, openmpi 最新版だと libfabric あたりでコードいろいろ変わったりで不安定っぽい)

$ sudo apt install openmpi-bin

ファイルシステムは理想は nfs or cifs(samba) で共通化ですが, とりあえず2ノードであれば両方で同じファイルレイアウトを使えば OK です.
(MPI で利用するファイルやディレクトリは rsync で同期しておくなどする)

benchmark

https://ulhpc-tutorials.readthedocs.io/en/latest/parallel/mpi/OSU_MicroBenchmarks/

OSU MicroBenchmarks を使います.
apt では用意されていないようなので, ソースからビルドします.

$ ./configure --prefix=$HOME/local/osu CC=mpicc CXX=mpicxx
$ make
$ make install

hostfile には IPoIB の IP をリストしておきます.

# hostfile
10.0.100.100
10.0.100.101

各ノードに ssh でパスフレーズなしでログインできるようにしておきます(~/.ssh/authorized_key に公開鍵登録)

デフォルトでは, ipoib の IP で mpirun すると

--------------------------------------------------------------------------
By default, for Open MPI 4.0 and later, infiniband ports on a device
are not used by default.  The intent is to use UCX for these devices.
You can override this policy by setting the btl_openib_allow_ib MCA parameter
to true.

とでることあります.

https://github.com/hpc-unibe-ch/ubelix-easyconfigs/issues/15#issuecomment-739955075

export OMPI_MCA_btl_openib_allow_ib=1

か, mpirun の引数で --mca btl_openib_allow_ib 1 を指定しておきます.
(--mca はいろいろ mpi 実行の設定をするオプション https://docs.open-mpi.org/en/main/mca.html)

しておきます.

ただ, ubuntu 20.04 だと libfabric のバグなのか, osu_bw など実行すると

[aero:30099] *** Process received signal ***
[aero:30099] Signal: Segmentation fault (11)
[aero:30099] Signal code:  (128)
[aero:30099] Failing at address: (nil)
[aero:30099] [ 0] /lib/x86_64-linux-gnu/libc.so.6(+0x43090)[0x7fbd3a70d090]
[aero:30099] [ 1] /lib/x86_64-linux-gnu/libibverbs.so.1(ibv_dereg_mr+0x20)[0x7fbd38978050]
[aero:30099] [ 2] /lib/x86_64-linux-gnu/libfabric.so.1(+0x536c7)[0x7fbd3844d6c7]
[aero:30099] [ 3] /lib/x86_64-linux-gnu/libfabric.so.1(+0x758d9)[0x7fbd3846f8d9]
[aero:30099] [ 4] /lib/x86_64-linux-gnu/libfabric.so.1(+0x759c1)[0x7fbd3846f9c1]
[aero:30099] [ 5] /lib/x86_64-linux-gnu/libfabric.so.1(+0x792b5)[0x7fbd384732b5]
[aero:30099] [ 6] /lib/x86_64-linux-gnu/libfabric.so.1(+0x7a52c)[0x7fbd3847452c]
[aero:30099] [ 7] /lib/x86_64-linux-gnu/libfabric.so.1(+0x7af71)[0x7fbd38474f71]
[aero:30099] [ 8] /lib/x86_64-linux-gnu/libfabric.so.1(+0x28475)[0x7fbd38422475]
[aero:30099] [ 9] /lib/x86_64-linux-gnu/libfabric.so.1(+0x27b02)[0x7fbd38421b02]

と segmentation fault します.

--mca mtl '^ofi' として, mtl(matching transport layer)で ofi を off (^ は disable を意味する. ' でクォートしておくと確実)にするととりあえずは動きます.

Ubuntu 22.04 ではこのバグは治っているかもしれません.

最終的にこんな感じ.

# hostfile 使う場合は -hostfile hostfile
$ mpirun --mca btl_openib_allow_ib 1 --mca mtl '^ofi' -np 2 -host 10.0.100.101,10.0.100.100 ~/local/osu/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw
# OSU MPI Bandwidth Test v7.1
# Size      Bandwidth (MB/s)
# Datatype: MPI_CHAR.
1                       2.41
2                       4.80
4                       9.60
8                      17.55
16                     35.49
32                     54.91
64                    109.68
128                   299.92
256                   602.31
512                  1114.86
1024                 2153.19
2048                 2654.70
4096                 3018.45
8192                 3198.11
16384                3426.45
32768                3478.47
65536                3504.48
131072               3517.39
262144               3524.05
524288               3527.37
1048576              3529.10
2097152              3530.00
4194304              3530.25

Voila! 3.5 GB/s と理論値(3.2 GB/s) + alpha 出ました!

pt2pt/osu_latency

# OSU MPI Latency Test v7.1
# Size          Latency (us)
# Datatype: MPI_CHAR.
1                       1.22
2                       1.21
4                       1.21
8                       1.30
16                      1.31
32                      1.33
64                      1.43
128                     2.61
256                     2.78
512                     2.99
1024                    3.42
2048                    4.28
4096                    5.15
8192                    6.48
16384                   9.05
32768                  13.45
65536                  22.84
131072                 41.30
262144                 78.23
524288                152.06
1048576               299.80
2097152               595.11
4194304              1186.97

~ 64 bytes まで 1.5 ns くらいでした. これもおおむね仕様通りでしょうか.

pytorch + NCCL とか

たぶん普通に使えるはず... T.B.W.

既存 socket アプリ

https://qiita.com/syoyo/items/a8fc2efa6d179242527a

rsocket(今は rspreload?)で行けます.
最近では librdmacm あたりのパッケージに標準で含まれており, ubuntu では /usr/lib/x86_64-linux-gnu/rsocket/librspreload.so にあります.

リンク先のスクリプトを python3 で書き換えて 1GB 転送処理してみたところ...

なにもなし: elap 1511 [ms]
rsocket preload: elap: 1670 [ms]

あんまり変わりませんでした. 何もなしでも 700 MB/sec くらいは出ています.
IPoIB connected mode にしたらなにか効果があるかもしれません.

TODO

  • OS 環境を合わせて MPI osu benchmark を走らせる
    • mpich 版を試す
  • network 名が PCI スロットの構成変わると変わる. NetworkManager で mac アドレスで固定する方法を探す

Discussion