モデルの学習にディープラーニングフレームワークのChainerを使用していますが、対局時にChainerで推論を行うと、Python経由で呼び出すためマルチGPUで動かす際、Python経由だとGILによってマルチスレッドの性能が出なくなる。
また、実行環境にPythonが必要になるため、実行バイナリを配布しても利用者側で環境構築が必要になってしまう。
それらの問題を解決するため、対局時にはChainerを使用しないで、cuDNNを直接しようして推論を行えるようにした。
実装方法の詳細は以下の記事を参照してほしい。
Chainerで学習したモデルを使ってcuDNNで推論する - TadaoYamaokaの開発日記
Chainerで学習したモデルを使ってcuDNNで推論する(時間計測) - TadaoYamaokaの開発日記
Chainerで学習したモデルを使ってcuDNNで推論する(BatchNormalization) - TadaoYamaokaの開発日記
Chainerで学習したモデルを使ってcuDNNで推論する(dropout) - TadaoYamaokaの開発日記
Chainerで学習したモデルを使ってcuDNNで推論する(ResNet) - TadaoYamaokaの開発日記
Chainerで学習したモデルを使ってcuDNNで推論する(マルチGPU) - TadaoYamaokaの開発日記
はじめ、キューに溜まっている要求がバッチサイズ未満でも固定のバッチサイズで推論を行うように実装したら、Python経由でChainerを呼び出すよりも遅くなってしまった。
初期局面の探索速度
playout/sec | |
Chainer | 4542 |
cuDNN | 4095 |
期待した結果にならなかったので、バッチサイズを可変にして、キューに溜まっているサイズのバッチサイズで推論を行うようにしたところ、Python経由でChainerを使用する場合を上回った。
初期局面の探索速度
playout/sec | |
Chainer | 4542 |
cuDNN | 4854 |
GPUの並列性を期待して、バッチサイズによらず速度は一定と考えたが、メモリの転送時間とかの影響があるため、バッチサイズは可変にした方が良いことがわかった。
しかし、MNISTの推論で比較した場合に比べて、期待したほど速度が向上していない。
ChainerはPythonで実行している分無駄があるので、もっと差が出てよさそうである。
Chainerは、速度を出すためにチューニングしているため、素朴な実装だとGPUの性能が出せていないと思われる。
速度を出すには、メモリ転送をStreamを使うなどチューニングが必要そうだ。
CUDAのチューニングノウハウをほとんど持っていないので、Chainerのソースをみるなどしてもう少し調査してみるつもりだ。
マルチGPUについては、効果があるはずなので、別途測定を行う予定。
2018/3/27 追記
メモリ転送をストリームを使って並列化してみたが、
playout/sec | |
直列 | 4853 |
ストリーム | 4202 |
という結果になり、かえって遅くなってしまった。
GPU計算中にメモリ転送できる箇所が入力層と出力層に一部しかなく、cudaStreamSynchronizeを実行する分、遅くなってしまったようだ。
ストリームに対応させる際に、ホストメモリの確保をC++のデフォルトヒープから、cudaHostAllocによって割り当てられるピンメモリに変更したが、そのことによって速くなることはなかった。
以上から、推論の実行時間のほとんどはGPUの計算に費やされており、Python経由でChainerから実行しても、cuDNNで直接実行しても差は限定的ということが言えそうだ。それでも、Chainerよりも7%程速くなっているので、少しは効果があった。