背景
コンテナを使う時よく問題になるのは、特殊なハードウエアを使う場合にデバイスドライバとそれを叩くライブラリの組み合わせ問題です。コンテナはカーネル空間を分離してユーザーランドだけで動きます。つまりカーネルとデバイスドライバは管轄外になります。一方でアプリケーションを動かすライブラリはユーザーランドつまりコンテナ内にあります。せっかくのポータビリティがコンテナにあっても、このミスマッチでアプリが動かないのでは困ります。HPCではGPUをはじめ、Infinibandだったり、特殊機材のハードウエアや他のマシンのメモリすら直接叩いてパフォーマンスを最大化してきた歴史があります。そもそもコンテナとは相性が悪いのです。
さて、ホスト側には(ちゃんと設定してあれば)特殊なハードウエアを使うためのデバイスドライバとライブラリが適切にインストールしてあることでしょう。そうでなければ電気を食うだけです。そこでホスト側に入っているライブラリをコンテナ内で使えるようにしようというアイデアが出てきます。
Singularityは、ホスト側のハードウエアのアクセス先である/devは共有しています。あとはライブラリがそろえば使えるはずです。最も需要が多いだろうと思われるNVIDIAのGPU対応として --nv
というオプションがかなり以前から用意されていました。これにより、ホスト側にインストールしてあるライブラリがコンテナ内に出現し、コンテナイメージにライブラリをインストールすることなく使えるようになりました。ただし、そのライブラリとアプリケーションが要求するバージョンが合っていないというケースはもちろんありえます。
さて、GPUにはNVIDIAだけでなくAMDの製品もあります。元々はATIという独立したグラフィックチップメーカーでしたが2006年にAMDに買収されて製品を作っています。カタログスペック上はNVIDIA製品よりも優れたラインナップも多かったのですが、これを使う開発用ソフトウエアが充実しているとは言えず、シェアは多くありません。ただし、あの手この手で使いやすくして高い性能を得ようという動きはあります。特にお金が絡んだ仮想通貨のマイニングなどでは安価で高性能なAMDのGPUは人気があり、専用のカードすら存在しました。仮想通貨ブームが去ったあと、秋葉原で投げ売りされていたのを思い出します。
当時まさにマイニングしていた専用カード(RX580)搭載マシンがたまたま使えたので、さくっと実験してみました。
環境づくり
基本的にAMDはオープンソース路線で展開していて、AMDのGPU環境を表す ROCmの情報は全てhttps://rocm.github.io/で入手可能です。git clone
するだけでサンプルも全て入手できます。
環境ができあがったら、正しく動作しているか見てみましょう。
$ rocm-smi --showproductname |grep model GPU[1] : Card model: Nitro+ Radeon RX 570/580/590 GPU[2] : Card model: Nitro+ Radeon RX 570/580/590 GPU[3] : Card model: Nitro+ Radeon RX 570/580/590 $ rocm-smi -M ========================ROCm System Management Interface======================== ================================================================================ GPU[1] : Max Graphics Package Power (W): 110.0 GPU[2] : Max Graphics Package Power (W): 110.0 GPU[3] : Max Graphics Package Power (W): 110.0 ================================================================================ ==============================End of ROCm SMI Log ==============================
なんと最大消費電力が110Wと、かなり低消費電力です。これが3枚です。ただしこのマシンのCPUはIntel Xeon Silver 4214とかなり控えめです。マイニング専用機だったのでここはしょうがありません。
さて、先程githubから落としてきた中にROCm/HIP-Examples/mini-nbody
というNVIDIAでもおなじみのnbodyベンチマークがあります。厳密には同じではありませんが、このディレクトリにはCUDAやIntel MICのコードも入っているあたり、並々ならぬ対抗意識が見て取れます。これをHIP(CUDAにかなり近い文法で記述できるAMDの独自規格)コンパイラでビルドします。スクリプトが入っているので、PATHさえ通っていればハマることはありません。ただし、そのまま走り始めてしまいます。スクリプトは実行する前に中味を確認しましょう。
$ ./HIP-nbody-orig.sh hipcc -I../ -DSHMOO nbody-orig.cpp -o nbody-orig ./nbody-orig 1024 1024, 2.370 ./nbody-orig 2048 2048, 5.533 ./nbody-orig 4096 4096, 12.015 ./nbody-orig 8192 8192, 24.999 ./nbody-orig 16384 16384, 35.966
これを実行中にrocm-smiを実行すると、
# rocm-smi ========================ROCm System Management Interface======================== ================================================================================ GPU Temp AvgPwr SCLK MCLK Fan Perf PwrCap VRAM% GPU% 1 46.0c 109.226W 1150Mhz 300Mhz 24.71% auto 110.0W 0% 100% 2 29.0c 33.231W 300Mhz 300Mhz 16.86% auto 110.0W 0% 0% 3 30.0c 35.141W 300Mhz 300Mhz 16.86% auto 110.0W 0% 0% ================================================================================ ==============================End of ROCm SMI Log ==============================
と、メモリクロックこそ上がりませんが、GPUを使って計算しています。プログラムが許容する最大数を設定してもメモリが使われていないように見えます。
Singularityで実行する
ここで本題です。リンクされているライブラリを見てみましょう
$ ldd nbody-orig linux-vdso.so.1 => (0x00007fff0edf8000) libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f4d904cd000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f4d902c9000) libm.so.6 => /lib64/libm.so.6 (0x00007f4d8ffc7000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f4d8fdab000) libhc_am.so.3 => /opt/rocm/hcc/bin/../lib/libhc_am.so.3 (0x00007f4d8fb1e000) libmcwamp.so.3 => /opt/rocm/hcc/bin/../lib/libmcwamp.so.3 (0x00007f4d8f2f5000) libhip_hcc.so => /opt/rocm/hip/lib/libhip_hcc.so (0x00007f4d8eae8000) libhsa-runtime64.so.1 => /opt/rocm/hsa/lib/libhsa-runtime64.so.1 (0x00007f4d8e817000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f4d8e601000) libc.so.6 => /lib64/libc.so.6 (0x00007f4d8e233000) /lib64/ld-linux-x86-64.so.2 (0x00007f4d907d4000) libamd_comgr.so.1 => /opt/rocm/lib/libamd_comgr.so.1 (0x00007f4d86747000) libhsakmt.so.1 => /opt/rocm/lib64/libhsakmt.so.1 (0x00007f4d86520000) libz.so.1 => /lib64/libz.so.1 (0x00007f4d8630a000) librt.so.1 => /lib64/librt.so.1 (0x00007f4d86102000) libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007f4d85ed8000) libnuma.so.1 => /lib64/libnuma.so.1 (0x00007f4d85ccd000) libpci.so.3 => /lib64/libpci.so.3 (0x00007f4d85ac0000) libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f4d858a7000)
/opt/rocm
以下のライブラリがダイナミックリンクされています。これをROCm環境が一切インストールされていないUBI7のコンテナを--rocm
オプションを付けて起動すると、その中でどう見えるか?を確認してみます。
$ singularity shell --rocm ~/ubi7.sif Singularity> ldd nbody-orig linux-vdso.so.1 => (0x00007ffd94b6d000) libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007ffb4e28c000) libdl.so.2 => /lib64/libdl.so.2 (0x00007ffb4e088000) libm.so.6 => /lib64/libm.so.6 (0x00007ffb4dd86000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007ffb4db6a000) libhc_am.so.3 => /.singularity.d/libs/libhc_am.so.3 (0x00007ffb4d8dd000) libmcwamp.so.3 => /.singularity.d/libs/libmcwamp.so.3 (0x00007ffb4d0b4000) libhip_hcc.so => /.singularity.d/libs/libhip_hcc.so (0x00007ffb4c8a7000) libhsa-runtime64.so.1 => /.singularity.d/libs/libhsa-runtime64.so.1 (0x00007ffb4c5d6000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007ffb4c3c0000) libc.so.6 => /lib64/libc.so.6 (0x00007ffb4bff2000) /lib64/ld-linux-x86-64.so.2 (0x00007ffb4e593000) libamd_comgr.so.1 => /.singularity.d/libs/libamd_comgr.so.1 (0x00007ffb44506000) libhsakmt.so.1 => /.singularity.d/libs/libhsakmt.so.1 (0x00007ffb442df000) libz.so.1 => /lib64/libz.so.1 (0x00007ffb440c9000) librt.so.1 => /lib64/librt.so.1 (0x00007ffb43ec1000) libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007ffb43c97000) libnuma.so.1 => /.singularity.d/libs/libnuma.so.1 (0x00007ffb43a8c000) libpci.so.3 => /.singularity.d/libs/libpci.so.3 (0x00007ffb4387f000) libresolv.so.2 => /lib64/libresolv.so.2 (0x00007ffb43666000)
/.singularity.d/libs 以下に必要なものが全てあることになっています。これらが本来インストールされている/opt以下には何も見えないにもかかわらずです。当然このまま実行できます。
次回はここにGROMACS-2020をインストールして実行してみます。