前回実装した、Chainerで学習したモデルを使用してcuDNNで推論するコードを、マルチGPUで並列に動作するようにした。
cuDNNをマルチスレッドで、スレッドに別々のGPUを割り当てて使用する場合、それぞれのスレッドでcudaSetDevice()を呼び出し、GPU IDを指定する。
cudaSetDevice()を呼び出し後に、cudnnCreate()を呼び出しcuDNNのハンドルを取得し、スレッドごとに異なるcuDNNのハンドルを使用する。
前回までのコードは、cudnnCreate()をNNクラスの静的クラス変数の初期化で呼び出していたが、上記の仕様に対応するため、NNクラスの初期化時に呼び出し、
スレッドごとにNNクラスのインスタンスを作成するように変更した。
以上の変更で、マルチGPUで動作できるようになった。
マルチGPUによって、高速化の効果があるか確認するために、以下の通り測定を行った。
測定条件
- スレッド内で初期化処理は時間に含めず、推論のみの時間を測定
- MNISTのtest set images(1000画像)のすべてを20回推論するのに要する時間を測定
- 3回測定した平均時間
- ミニバッチサイズは、100
- モデルは畳み込み1層+畳み込み1層のResNetブロック1つ+畳み込み1層+全結合層2層、BatchNormalizationあり
- GPUは2枚(TitanVとGeForce GTX 1080 Ti)
- CUDA 9.0、cuDNN 7.0を使用
測定結果
Chainer(シングルGPU) | 4767 ms |
シングルGPU | 1657 ms |
マルチGPU | 915 ms |
比較対象として、Chainerでシングルスレッドで推論した時間、cuDNNでシングルスレッドシングルGPUで推論した時間を記載している。
マルチGPUは、シングルGPUの約1.8倍高速になっている。
以前に将棋AIの推論でChainerを使ってマルチGPUに対応させたが、マルチGPU化の効果はほとんどなかった。
ChainerをPython経由で使用しているため、PythonのGIL機構によりマルチスレッドでの性能がでないためである。
GPUごとの推論時間
mnistCUDNN\mnistCUDNN>..\x64\Release\mnistCUDNN.exe 2 There are 2 CUDA capable devices on your machine : device 0 sms 80 Capabilities 7.0, SmClock 1455 Mhz, MemSize (Mb) 12288, MemClock 850 Mhz, Ecc=0, boardGroupID=0 device 1 sms 28 Capabilities 6.1, SmClock 1670.5 Mhz, MemSize (Mb) 11264, MemClock 5505 Mhz, Ecc=0, boardGroupID=1 gpu:0 1000 iterations 867 [ms] gpu:1 1000 iterations 751 [ms] total time = 894 [ms]
gpu:0がTitan Vで、推論に867 [ms]を使っている。
gpu:1がGeForce GTX 1080 Tiで、推論に751 [ms]を使っている。
Titan Vの方が遅いため、トータルの時間は、Titan Vの実行時間+αになっている。
Titan Vの方が遅いのは、クロック数の違いによる。
ベースクロック (MHz) | メモリクロック (MHz) | |
Titan V | 1200 | 850 |
Geforce GTX 1080 Ti | 1480 | 1100 |
モデルのサイズ、バッチサイズが小さい場合は、Geforce GTX 1080 Tiの方が高速に動作する。
Titan Vの性能を引き出せるのは、サイズの大きいモデルでバッチサイズを大きくして学習する場合である。
推論の速度だけであれば、モデルによってはクロック数の高いGeforce GTX 1080 Tiを複数枚用意した方がよさそうである。
測定に使用したソースコードを公開しました。
github.com