AWS-GPUとスパコンを比較する方法(P2インスタンス編)-スパコン用ベンチマークソフトを動かしてみる

前に、[お手元のマシンとスパコンを比較する方法](http://qiita.com/shibacow/items/753e1d46882152e46757)と言うなんともアホっぽい記事を書いた。
ついでに、awsのg2インスタンスを使ってGPUの評価をしたらどうかという趣旨で、[AWS-GPUとスパコンを比較する方法](http://qiita.com/shibacow/items/aa83cfc81ed493a4c25d)という記事を書いた。その時はg2インスタンスだったが、最近、AWSで[p2インスタンス](https://aws.amazon.com/jp/ec2/instance-types/p2/)という新しいGPUインスタンスが追加された。ついでなので、こちらでも評価してみよう。
先の記事と同じように、LINPACKベンチマークをaws p2インスタンス(CUDA)で動かしてみる。
スパコンは、メモリもストレージもネットワーク性能も段違いに高性能なので、真に受けないように。

ベンチマークの内容を詳しく分かっているわけではないので、間違っているかも知れない。
多くの仕組みは、前回の記事[AWS-GPUとスパコンを比較する方法](http://qiita.com/shibacow/items/aa83cfc81ed493a4c25d)を参考にしている。

# AWS p2.xlargeでLINPACK ベンチマークを動かす。
という訳で、AWSで最もCPUが早そうなマシンを使って、スパコンと性能の比較をしてみる。

今回使用したマシンはこちら

p2.xlarge

|モデル|vCPU|メモリ|GPU|
|:--:|:--:|:--:|:--:|
|p2.xlarge|4core|62GB|1|

4992 NVIDIA CUDA cores with a dual-GPU design
Up to 2.91 Teraflops double-precision performance with NVIDIA GPU Boost - See more at: http://www.nvidia.com/object/tesla-k80.html#sthash.K2miMs1X.dpuf

# LINPACKベンチマーク環境の構築

CUDA実行環境の構築は結構大変だ。
[AWS-GPUとスパコンを比較する方法](http://qiita.com/shibacow/items/aa83cfc81ed493a4c25d)を参考にする


## linpackプログラムの準備

https://devtalk.nvidia.com/default/topic/819284/linpack-benchmark-for-cuda/
こちらを参考。CUDA Registered Developer Programというのに入って、上のリンク
https://developer.nvidia.com/rdp/assets/cuda-accelerated-linpack-linux64
こちらから、```hpl-2.0_FERMI_v15.tgz```を手に入れる。

## インスタンス立ち上げ

nvidiaが用意しているCUDAドライバーが事前に入っている環境を利用する。

AWS marketplaceで「grid」で検索すると出てくる

![gridenv](https://i.gyazo.com/6d42a5420687ccb6c4485d95c49e4960.png)

Amazon Linux AMI with NVIDIA GRID GPU Driverを利用する。
オレゴンにしか無かったので、AMI ID amzn-ami-graphics-hvm-2016.09.0.20161028-x86_64-ebs-d3fbf14b-243d-46e0-916c-82a8bf6955b4-ami-0d38601a.3 (ami-caf253aa) を利用。

## 初期設定

次のものを入れる

```
sudo yum groupinstall -y "Development Tools"
sudo yum install -y emacs
sudo yum install -y tmux
sudo yum install -y openmpi openmpi-devel
sudo ln -s /opt/nvidia/cuda /usr/local/cuda

```

## INTEL MKLを利用する。

次のページからインテルのMath Libraryを取得する。ついでに利用するシリアルナンバーを用意する。
https://software.intel.com/en-us/qualify-for-free-software

登録すると、シリアルナンバーを送ってくれるのでそれを用意する。

```l_mkl_2017.0.098.tgz```などが、手に入るので、展開して ```install.sh```を実行する。対話的なやり取りを(その際シリアルナンバーを打ち込む)する。シリアルナンバーを打ち込む以外は、エンターを押せばOK.展開時に、```silent.cfg```が展開されるのでそちらを設定してもよい。やり方は[こちら](http://qiita.com/unnonouno/items/8ab453a1868d77a93679)。

シリアル番号は、Math Kernelのバージョンが変わると変えなければならないようだ。

デフォルトで、```/opt/intel```以下にインストールされる。

## LINPACKベンチマークの作成

関係するものは[こちら](https://gist.github.com/shibacow/be89c848445d816e8283e82eccf1f41a)。

### hpl-2.0_FERMI_v15.tgz展開

hpl-2.0_FERMI_v15.tgzを展開する。

どこでも良いが、```/home/ec2-user/```以下に展開する(以下、それを前提とする)。

```shell-session
[ec2-user@ip-xxx-xxx-xxx-xxx hpl-2.0_FERMI_v15]$ ls
bin                      HISTORY  Make.CUDA      makes     setup    TUNING
BUGS                     include  Make.CUDA~     Make.top  src      www
COPYRIGHT                INSTALL  Make.CUDA.org  man       testing
CUDA_LINPACK_README.txt  lib      Makefile       README    TODO
```

### 環境設定の作成
~/.bashrcに以下を追加

```
# User specific aliases and functions
export LD_LIBRARY_PATH=/opt/intel/mkl/lib/intel64:/opt/intel/compilers_and_libra
ries/linux/lib/intel64:/home/ec2-user/hpl-2.0_FERMI_v15/src/cuda:/usr/lib64/open
mpi/lib:/opt/intel/compilers_and_libraries_2017.0.098/linux/compiler/lib/intel64
_lin
export PATH=/usr/lib64/openmpi/bin:$PATH
[ec2-user@ip-10-0-0-192 ~]$
```

前回との違いは、```/opt/intel/compilers_and_libraries_2017.0.098/linux/compiler/lib/intel64_lin```を追加したことだ。

```
source ~/.bashrc
```

### Make.CUDAの編集

殆どはMake.CUDAの編集をすれば良い

サンプルは[こちら](https://gist.github.com/shibacow/be89c848445d816e8283e82eccf1f41a#file-make-cuda)

上を編集して、前回との違いは、
```
LAMP5dir= /opt/intel/compilers_and_libraries_2017.0.098/linux/compiler/lib/intel64_lin
```
を追加したことにある。

```
make
```
を実行すれば、```bin/CUDA```に```xhpl```が出来上がる.lddでsoへのパスが通っているか確認してください。

### run_linpackの編集

bin/CUDA以下にあるrun_linpackの編集を行なう

```
#location of HPL
HPL_DIR=/home/ec2-user/hpl-2.0_FERMI_v15
```
と
```
CPU_CORES_PER_GPU=4
```
にする。GPUが一つで、CPUが4つなので4を指定する。


## HPL.datの編集

ベンチマークはHPL.datの編集を行なう。そのままでは動かない。PsとQsを1に指定する。
サンプルは[こちら](https://gist.github.com/shibacow/be89c848445d816e8283e82eccf1f41a#file-hpl-dat)にある

```
2           # of problems sizes (N)
65536 81920 100000 110000 160000 180000 39007 39000  20960 364160 359424 276480 138240 115200 23040 354432 236160 95040 9600 20737 16129 16128 Ns
2             # of NBs
768 1536 640 768 896 960 1024 1152 1280 384 640 960 768 640 256 960 512 768 11\52 NBs
```
上の2は、65536 81920までやるという意味っぽい
4は768 1536までやるという意味だ。
最適な場所を探すと、GFlopsの最適値がわかると思う。

# LINPACKベンチ実行

```
mpirun -np 1 -host localhost ./run_linpack
```

 と実行すれば、ベンチマークが走る

# 実行結果

```shell-session
================================================================================
T/V                N    NB     P     Q               Time                 Gflops
--------------------------------------------------------------------------------
WR10L2L4       65536   768     1     1             242.50              7.738e+02
--------------------------------------------------------------------------------
||Ax-b||_oo/(eps*(||A||_oo*||x||_oo+||b||_oo)*N)=        0.0038208 ...... PASSED
================================================================================
T/V                N    NB     P     Q               Time                 Gflops
--------------------------------------------------------------------------------
WR10L2L4       65536  1536     1     1             235.32              7.975e+02
--------------------------------------------------------------------------------
||Ax-b||_oo/(eps*(||A||_oo*||x||_oo+||b||_oo)*N)=        0.0043131 ...... PASSED
================================================================================
T/V                N    NB     P     Q               Time                 Gflops
--------------------------------------------------------------------------------
WR10L2L4       81920   768     1     1             449.06              8.162e+02
--------------------------------------------------------------------------------
||Ax-b||_oo/(eps*(||A||_oo*||x||_oo+||b||_oo)*N)=        0.0040796 ...... PASSED
================================================================================
T/V                N    NB     P     Q               Time                 Gflops
--------------------------------------------------------------------------------
WR10L2L4       81920  1536     1     1             454.85              8.058e+02
--------------------------------------------------------------------------------
||Ax-b||_oo/(eps*(||A||_oo*||x||_oo+||b||_oo)*N)=        0.0047358 ...... PASSED
================================================================================

```

長いので省略した。全部の結果は[こちらのgist](https://gist.github.com/shibacow/be89c848445d816e8283e82eccf1f41a#file-xhpl_benchmark_result)にある .

結果を見ると、800GFlops位になりそうだ。実際のところGPUには余力がありそうだが、Nが8万を超えたあたりで、メインメモリーが、パンクしてしまった。理論値では、2.91TFlopsまで行くはずなので、メモリが許せばまだ性能が出るとおもわれる。

ちなみに、その時にGPUの使用率はこんなもん。nvidea-smiで状態を把握する。

```
$nvidia-smi -q  -d UTILIZATION -l
```

## CPUだけの場合

GPUを使わず、CPUだけの場合どの程度のGFlopsが出るのか？
```run_linpack```で次の設定をする。```CUDA_DGEMM_SPLIT```で0.0を設定する。

```
# hint: for 2050 or 2070 card
#       try 350/(350 + MKL_NUM_THREADS*4*cpu frequency in GHz)
export CUDA_DGEMM_SPLIT=0.00

# hint: try CUDA_DGEMM_SPLIT - 0.10
export CUDA_DTRSM_SPLIT=0.00
```

実行結果

```
================================================================================
T/V                N    NB     P     Q               Time                 Gflops
--------------------------------------------------------------------------------
WR10L2L4       32032   512     1     1             307.91              7.117e+01
--------------------------------------------------------------------------------
||Ax-b||_oo/(eps*(||A||_oo*||x||_oo+||b||_oo)*N)=        0.0046919 ...... PASSED
================================================================================
````

CPUだけだと。71GFlops

p2.xlargeのcpuは```Intel(R) Xeon(R) CPU E5-2686 v4 @ 2.30GHz```が4coreだ。

E5-2686そのものの資料は無いが、[こちらの資料](http://jp.fujitsu.com/platform/server/primergy/performance/pdf/wp-performance-report-primergy-rx350-s7-ww-ja.pdf)より大体、理論値では211GFlopsあることになる。計測値では、71GFlopsなので大体33%程度だと思われる。

![E5-2686](https://i.gyazo.com/7c205e516849af8c8c7c6783ec84d478.png)

# 追記、p2.8xlargeでも試してみる

上の例では、p2.xlargeで試した。そちらはGPUはⅠつだ。GPUを増やしてみたらどうなるか試した。

p2インスタンスは、p2.xlargeの他にp2.8xlarge(8GPU),p2.16xlarge(16GPU)のインスタンスがある。p2.8xlargeで試してみた。

p2.8xlargeのスペックはこちら。

|モデル|vCPU|メモリ|GPU|GPUメモリ|
|:--:|:--:|:--:|:--:|:--:|
|p2.8xlarge|32core|488GB|8|96GB|

4992 NVIDIA CUDA cores with a dual-GPU design
Up to 2.91 Teraflops double-precision performance with NVIDIA GPU Boost - See more at: http://www.nvidia.com/object/tesla-k80.html#sthash.K2miMs1X.dpuf

とのことなので、2.91*8=24TFlopsが最大なのかしら。

## p2.xlargeと変更した点

processが一つだと、1つのGPUしか使わない。また、HPLのPsとQsも1x1と指定しなければならなかった。しかし、今回は8つのGPUがあるので次のように指定した。

```
mpirun -np 8 -host localhost ./run_linpack
```

プロセス数は8にした。また、HPL.datは以下のように変更

```
0            PMAP process mapping (0=Row-,1=Column-major)
1            # of process grids (P x Q)
2       Ps
4       Qs
16.0         threshold
```

Ps=2 x Qs=4 => 8 なので```mpirun -np 8 ``` を指定すれば、8つのGPUに分割して計算してくれるようだ。

8つのGPUが動いているのは```nvidia-smi -l ```を見ると、きちんと動いているのがわかる。

![](https://i.gyazo.com/dce4f477ddf6b5f2df447f478ae8ab69.png)

## p2.8xlargeでのLinpackを動かした結果

```
================================================================================
T/V                N    NB     P     Q               Time                 Gflops
--------------------------------------------------------------------------------
WR10L2L4       65536   768     2     4             126.13              1.488e+03
--------------------------------------------------------------------------------
||Ax-b||_oo/(eps*(||A||_oo*||x||_oo+||b||_oo)*N)=        0.0030666 ...... PASSED
================================================================================
T/V                N    NB     P     Q               Time                 Gflops
--------------------------------------------------------------------------------
WR10L2L4       65536  1536     2     4             106.63              1.760e+03
--------------------------------------------------------------------------------
||Ax-b||_oo/(eps*(||A||_oo*||x||_oo+||b||_oo)*N)=        0.0033980 ...... PASSED
================================================================================
T/V                N    NB     P     Q               Time                 Gflops
--------------------------------------------------------------------------------
WR10L2L4       65536  2048     2     4              90.45              2.075e+03
--------------------------------------------------------------------------------
||Ax-b||_oo/(eps*(||A||_oo*||x||_oo+||b||_oo)*N)=        0.0032682 ...... PASSED
================================================================================
T/V                N    NB     P     Q               Time                 Gflops
--------------------------------------------------------------------------------
WR10L2L4       81920   768     2     4             231.72              1.582e+03
--------------------------------------------------------------------------------
||Ax-b||_oo/(eps*(||A||_oo*||x||_oo+||b||_oo)*N)=        0.0029593 ...... PASSED
================================================================================
T/V                N    NB     P     Q               Time                 Gflops
--------------------------------------------------------------------------------
WR10L2L4       81920  1536     2     4             184.96              1.982e+03
--------------------------------------------------------------------------------
||Ax-b||_oo/(eps*(||A||_oo*||x||_oo+||b||_oo)*N)=        0.0031443 ...... PASSED
================================================================================
T/V                N    NB     P     Q               Time                 Gflops
--------------------------------------------------------------------------------
WR10L2L4       81920  2048     2     4             151.98              2.412e+03
--------------------------------------------------------------------------------
||Ax-b||_oo/(eps*(||A||_oo*||x||_oo+||b||_oo)*N)=        0.0030877 ...... PASSED
================================================================================
```

ついに、2,412GFlops = 2.4TeraFlopsになった。2TFlopsを超えると、なんかスパコン感ある。ちなみに、[macbookでやったとき](http://qiita.com/shibacow/items/753e1d46882152e46757)には、37GFlopsなので、GPUを活用することでおよそ65倍位早くなる。

benchmarkの詳細は[こちら](https://gist.github.com/shibacow/be89c848445d816e8283e82eccf1f41a#file-xhpl_benchmark_result)。

## 16年前のスパコンと肩を並べる

上の例で、Linpackのベンチマークで、2412GFlopsが出た。これは何かと比較可能だろうか？スーパーコンピュータの世界では、半年に一度、[top500](https://www.top500.org/)と言って、スパコンの早い順位500位までリスト化している。調べてみたら、ちょうど[2000年の6月のリスト](https://www.top500.org/lists/2000/06/)の1位が、Sandia National Laboratories(US)で、システムが、ASCI Redで、その時の計算スピードが、2375GFlopsで大体近いのではないかと思った。つまり、大体16年前のスパコンレベルの計算能力をアマゾンでも借りることが出来るようになったのだろう(あくまでCUDAとかGPUを使ってそのくらいの演算性能になったというだけで、メモリとかストレージとかその他の信頼性はぜんぜん違うので、あまりまにうけないように)。

### NBs(BlockSize)について

NBsを大きくしていったら早くなったので、欲をかいて、NBsを4096まで行けないか試してみた。

|N|NBs|GFlops|
|:--:|:--:|:--:|
|65536|4096|1437|
|65536|3072|1993|
|65536|2048|2162|
|65536|1536|2079|
|65536|768|1482|

となり、NBsの値は、2048が一番成績が良かった。なんでも欲をかくとだめですね。


## 関連エントリー

[AWSとスパコンを比較する方法-スパコン用ベンチマークソフトを動かしてみる](http://qiita.com/shibacow/items/08490ac51aeb0d131d2e)
[お手元のマシンとスパコンを比較する方法-スパコン用ベンチマークソフトを動かしてみる](http://qiita.com/shibacow/items/753e1d46882152e46757)
[AWS-GPUとスパコンを比較する方法](http://qiita.com/shibacow/items/aa83cfc81ed493a4c25d)