kazuito_itokazu Blog

日本オラクルでインフラ周りの製品を触っています

Oracle Cloud のベアメタル GPU マシンで HPCG を動かしてみる

はじめに

HPCG という HPC ベンチマークGPU で計測する必要があったのでセットアップから計測までをメモしておきたいと思います。

目的

HPCG を複数のGPUノードで実行・計測する

環境

Oracle Cloud の GPU ベアメタルマシンを3台利用します。
BM.GPU2.2 というインスタンスで、CPU が Xeon 8167M x2 でメモリが 192GB のベアメタルマシンに NVIDIA Tesla P100 が2個搭載されています。
詳細はこちら → https://cloud.oracle.com/compute/gpu/features

HPCG について

HPCG はソースとバイナリの両方が提供されています。
今回はバイナリを利用したいと思います。

http://www.hpcg-benchmark.org/software/index.htmlGPU 向けのバイナリがありますので、HPCG 3.1 Binary for NVIDIA GPUs Including Volta(2017-10-16) を利用します。

このバイナリは以下の環境でコンパイルされているので、これに近い環境をセットアップしていきます。
CUDA: ver 9.0.176
OpenMPI: 1.10.2

セットアップ

ここでは Oracle Cloud 側のセットアップ(ネットワーク、GPU インスタンスの作成)については省略します。
OS は CentOS を選ぶこともできますが、Oracle Linux 7.5 を利用します。また、CentOS でも同じ流れでセットアップできると思いますが、一部インストールするパッケージ名がことなるので注意が必要です。

GPU の確認

まずは GPU が認識されているかを確認しておきます。

lspci | grep -i nvidia

以下のように P100 が2つ搭載されていることが確認できます。

5e:00.0 3D controller: NVIDIA Corporation GP100GL [Tesla P100 SXM2 16GB] (rev a1)
86:00.0 3D controller: NVIDIA Corporation GP100GL [Tesla P100 SXM2 16GB] (rev a1)

Kernel devel と header をインストール

CUDA のインストールで DKMS を使ってドライバが Build されるようなので、これらをインストールしておきます。
※このへんは CentOS では違うので注意

sudo yum install -y kernel-uek-devel-`uname -r` kernel-headers

OpenMPI のインストール

複数ノードで実行するので OpenMPI をインストールします。

sudo yum install -y openmpi.x86_64 openmpi-devel.x86_64

CUDA のインストール

CUDA を rpm でインストールをしていきます。
ここでは HPCG がコンパイルされた環境に合わせるため、CUDA 9.0 を指定してインストールしていきます。

sudo yum install -y http://developer.download.nvidia.com/compute/cuda/repos/rhel7/x86_64/cuda-repo-rhel7-9.0.176-1.x86_64.rpm
sudo yum install -y cuda-9-0.x86_64

以上が正常に完了したら一度 Reboot します。

sudo reboot

ここで /dev/nvidia0, /dev/nvidia1 などが認識されているか確認しますが、認識されていなかったので以下のスクリプトを実行して作成します。reboot しても有効になるように /etc/rc.local などに設定しておくとよいでしょう。

cat startup_nvidia.sh

#!/bin/bash

/sbin/modprobe nvidia

if [ "$?" -eq 0 ]; then
  # Count the number of NVIDIA controllers found.
  NVDEVS=`lspci | grep -i NVIDIA`
  N3D=`echo "$NVDEVS" | grep "3D controller" | wc -l`
  NVGA=`echo "$NVDEVS" | grep "VGA compatible controller" | wc -l`

  N=`expr $N3D + $NVGA - 1`
  for i in `seq 0 $N`; do
    mknod -m 666 /dev/nvidia$i c 195 $i
  done

  mknod -m 666 /dev/nvidiactl c 195 255

else
  exit 1
fi

/sbin/modprobe nvidia-uvm

if [ "$?" -eq 0 ]; then
  # Find out the major device number used by the nvidia-uvm driver
  D=`grep nvidia-uvm /proc/devices | awk '{print $1}'`

  mknod -m 666 /dev/nvidia-uvm c $D 0
else
  exit 1
fi

root で実行します。

sudo ./startup_nvidia.sh

これにより /dev 配下に以下のデバイスが作成されたことを確認します。

$ ls /dev/nvidia*
/dev/nvidia0  /dev/nvidia1  /dev/nvidiactl  /dev/nvidia-uvm  /dev/nvidia-uvm-tools

環境変数の設定

OpenMPI と CUDA の PATH と LD_LIBRARY_PATH を HPCG を実行するユーザの .bashrc と .bash_profile に追加します。

PATH=/usr/lib64/openmpi/bin:$PATH
LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:/usr/local/cuda-9.0/targets/x86_64-linux/lib

export PATH LD_LIBRARY_PATH

NFS Server の設定

複数ノードで HPCG を実行するにあたり、OpenMPI 経由で並列実行します。
OpenMPI から複数ノードで実行する際は、バイナリが同一ディレクトリにある必要があるので NFS を利用し実現します。

Oracle Cloud には NFS as a Service があるためこれを利用します。詳細は省きますが、Cloud の画面から簡単に NFS サービスをデプロイできます。
https://blogs.oracle.com/cloud-infrastructure/introducing-oracle-cloud-infrastructure-file-storage-service
上記で作成した share を /mnt/hpcg にマウントしました。
また、こちらも reboot しても大丈夫なように /etc/fstab に追加しておきます。

HPCG

以上で、HPCGを動かす環境が整いましたので、HPCG の実行をしてみます。

HPCG のダウンロードと展開

マウントした NFS Share 配下にダウンロードします。

cd /mnt/hpcg
wget http://www.hpcg-benchmark.org/downloads/hpcg-3.1_cuda9_ompi1.10.2_gcc485_sm_35_sm_50_sm_60_sm_70_ver_10_8_17.tgz

tar xvf hpcg-3.1_cuda9_ompi1.10.2_gcc485_sm_35_sm_50_sm_60_sm_70_ver_10_8_17.tgz

実行(シングルノード)

まずはシングルノードで実行してみます。
オプションには以下の2つを指定。
Infiniband ではなく Ethernet で実行するので --mca btl tcp,sm,self オプションをつけます。
また -np 2 で P100 x2個の両方で実行します。

cd hpcg-3.1_cuda9_ompi1.10.2_gcc485_sm_35_sm_50_sm_60_sm_70_ver_10_8_17
mpirun -np 2 --mca btl tcp,sm,self ./xhpcg-3.1_gcc_485_cuda90176_ompi_1_10_2_sm_35_sm_50_sm_60_sm_70_ver_10_8_17

nvidia-smi -l でGPUで処理しているプロセスが表示されることを確認します。

nvidia-smi -l

Tue Aug 14 15:38:10 2018       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 396.44                 Driver Version: 396.44                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla P100-SXM2...  Off  | 00000000:5E:00.0 Off |                    0 |
| N/A   41C    P0    41W / 300W |   8403MiB / 16280MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   1  Tesla P100-SXM2...  Off  | 00000000:86:00.0 Off |                    0 |
| N/A   41C    P0    43W / 300W |   8403MiB / 16280MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0      6995      C   ...0_2_sm_35_sm_50_sm_60_sm_70_ver_10_8_17  8391MiB |
|    1      6996      C   ...0_2_sm_35_sm_50_sm_60_sm_70_ver_10_8_17  8391MiB |
+-----------------------------------------------------------------------------+

うまくいくと以下のように処理が進みます。

start of application (2 OMP threads)...
2018-08-13 15:34:06.375

Problem setup...
Setup time: 0.377932 sec

GPU: 'Tesla P100-SXM2-16GB'
Memory use: 8315 MB / 16280 MB
2x1x1 process grid
256x256x256 local domain

Reference SpMV+MG...

Reference CG...
Initial Residual: 7.303536e+03 Max_err: 1.000000e+00 tot_err: 5.792619e+03
REF  Iter = 1 Scaled Residual: 1.875554e-01 Max error: 1.000000e+00 tot_error: 9.748362e-01
REF  Iter = 2 Scaled Residual: 1.031535e-01 Max error: 1.000000e+00 tot_error: 9.515092e-01
REF  Iter = 3 Scaled Residual: 7.120013e-02 Max error: 1.000000e+00 tot_error: 9.286195e-01

~ 省略 ~

Number of CG sets:      20
Iterations per set:     52
scaled res mean:        1.701928e-03
scaled res variance:    0.000000e+00

Total Time: 6.146707e+01 sec
Setup        Overhead: 1.21%
Optimization Overhead: 0.54%
Convergence  Overhead: 3.85%

2x1x1 process grid
256x256x256 local domain
SpMV  =  xxx.x GF (xxxx.x GB/s Effective)   xx.x GF_per ( xxx.x GB/s Effective)
SymGS =  xxx.x GF (xxxx.x GB/s Effective)  xxx.x GF_per ( xxx.x GB/s Effective)
total =  xxx.x GF (xxxx.x GB/s Effective)  xxx.x GF_per ( xxx.x GB/s Effective)
final =  xxx.x GF (xxxx.x GB/s Effective)   xx.x GF_per ( xxx.x GB/s Effective)

end of application...
2018-08-13 15:39:17.102

最後の方に結果が出力されます。※ここでは諸事情により数値は xxx.x に変えています。

同一ディレクトリ内に readme に P100 x2 のベンチマーク結果が載っていて、これとほぼほぼ同じなのでうまく動いているようです。

2 x P100

2x1x1 process grid
256x256x256 local domain
SpMV  =  167.0 GF (1051.8 GB/s Effective)   83.5 GF_per ( 525.9 GB/s Effective)
SymGS =  231.4 GF (1786.3 GB/s Effective)  115.7 GF_per ( 893.1 GB/s Effective)
total =  212.9 GF (1614.7 GB/s Effective)  106.5 GF_per ( 807.3 GB/s Effective)
final =  201.2 GF (1525.7 GB/s Effective)  100.6 GF_per ( 762.9 GB/s Effective)

実行(マルチノード)

マルチノード実行の設定ポイントとしては以下がありました。

  1. 作成した NFS の Share をすべてのノードでマウントする
  2. すべてのノードに .bashrc に PATH, LD_LIBRARY_PATH を入れる
  3. 実行する Master から他のノードへは Non Password で SSH ログインできるようにする
  4. Firewalld のポートは不明だったので、一旦Stopさせる
  5. SELinux は有効な状態で動いた

ノードの作成

並列で動かすサーバを作成します。
今回は Cloud なので、シングルノードのイメージを複製します。こちらも詳細は省きます。
これによりポイントの 1, 2 については省略することができます。
シングルノードのイメージから2台、GPU ベアメタルマシンを作成し、合計3台になりました。
以降、元々のノードをマスターノード、複製した2台をスレーブノードと書いていきます。

ssh Non-Password Login 設定

マスターノードで ssh key を作成し、public key をスレーブノードの ~/.ssh/authorized_keys に追加します。

ssh-keygen  -N '' -t rsa -b 2048

firewalld の停止

OpenMPI が利用するポート調べるとある程度の範囲のポートを開ける必要がある、とのことでした。
今回は一時的な利用なので、ポートを開放するのではなく、firewalld を停止させることにしました。
全ノードで以下を実行します。

sudo systemctl stop firewalld 

マルチノードで HPCG を実行

まずは確認としてマスターとスレーブの2台で実行してみます。
シングルノードで動かしたときのコマンドに --host master,slave01 を追加して実行します。
これで指定したノードの GPU x1 で HPCG が走ります。

mpirun --mca btl tcp,sm,self -np 2 --host master,slave01 ./xhpcg-3.1_gcc_485_cuda90176_ompi_1_10_2_sm_35_sm_50_sm_60_sm_70_ver_10_8_17

nvidia-smi -l で指定したノードでプロセスが実行されていることを確認します。

続いて3台のすべての GPU を利用して動作させます。
host ファイルを作成し以下のように hostname slots=GPU数 を記述します。
ここで書くhostname は /etc/hosts または DNS で解決できる必要があります。

master slots=2
slave01 slots=2
slave02 slots=2

今度は -hostfile オプションで上記作成した host ファイルを指定します。
これで、3ノードにまたがって合計6GPU でHPCG を走らすことができます。

mpirun --mca btl tcp,sm,self -np 6 -hostfile host ./xhpcg-3.1_gcc_485_cuda90176_ompi_1_10_2_sm_35_sm_50_sm_60_sm_70_ver_10_8_17

最後に

GPU の利用や OpenMPI など初めてなことばかりでしたが、なんとか動かす事ができました。
主なポイントはおさえて書けていると思いますが、細かいところで抜けがあるかもしれません。
近々、NVIDIA Tesla V100 で同じことを行いますので、不備がありましたらそこで修正していきたいと思います。