studylog/北の雲

chainer/python/nlp

Chainerメモ11 GPUで速度が出ない時

GPUなのに学習速度があまり速くならない、あるいはCPUより遅い時ってありませんか?

そういうとき自分はまず「nvidia-smi -l 1」でGPUの使用率を見て100%に近い値を維持できているかどうかチェックします。NVIDIA System Management Interfaceというものらしいです*1-lオプションに数値を指定するとn秒間隔でループしてその時のGPUの状態を出力してくれます。よく見る項目はGPU使用率、メモリ使用量、温度あたりでしょうか。

この使用率が低ければ低いほど効率的にGPU計算できていないことになります。計算以前のところがボトルネックになっている可能性が高い。

list → numpy or cupyへの変換速度で差が出る

pythonのリストをchainerで使えるようにnumpy or cupyに変換する時の速度が両者でだいぶ異なるようです。後者の方が遅い。

1.2以前のpycuda版でも同様なのでこれはchainerの問題では無く、もうそういうものだと思って諦めるしかないっぽいです。

このあたりを意識しないとせっかくのGPUなのに損します。

  • listからcupy.ndarrayへの変換をいかに少なくするか(同じ変換を二度としない epochごとに変換してたら無駄になる)
  • numpy/cupyの行列操作を適切に使う
  • ifやforをなるべく使わない、ブロードキャスティングをしっかり使う

酷い例

誰もこんな酷いコード書かないかもしれませんが、致命的に遅い例として自分が実際にやっちゃった例を。

a = [1,2,3]

[[1,2,3]]

にしたい時、python本体だったらa = [a]で問題ありません。numpyの場合も

a = np.asarray([1,2,3])
a = np.asarray([a]) #一度pythonのlistで囲ってから再度変換

と無理やりやってしまってもそこまで致命的な速度ロスは無いんですが、同じようなことをcupyでやると場合によっては100倍以上時間がかかります。普段からnumpyをバリバリに使っている人からすればお茶を吹くレベルのありえないコードでしょうがちょっと前までの自分はこんなコードがよく紛れ込んでました。

GPUなのに逆に遅くなった人はこういうnumpy/cupyの流儀から外れたコードを書いている恐れがあるので見なおしてみてください。pythonとは別の言語だと思って臨んだ方がいいようです。

numpyにあるけどcupyに無いので変換

anyやallなどがまだcupyには無いのでnumpyに変換する必要がある。こういうところでもじわじわと損をします。この2つは10月中旬のバージョン1.4で実装予定だそうです。ありがたい。

タイトルの内容は以上です。

タイトルと関係ないメモ GPUでモデル保存するときのやり方とメモリの挙動

(これは現時点1.3.2の情報です。モデル保存が公式サポートされる1.4以降では変わっている可能性が高いので注意してください。)

メモリをギリギリまで使って学習させている時にモデルを保存しようとして落ちたことないですか?やり方によって挙動が異なるようです。modelにはFunctionSetが入っていることを想定して、

#1  GPUのまま保存なのでCPUからは使えない メモリを消費しない 
pickle.dump(model, open(filename, 'wb'))

#2  コピーしたものをCPUに変換して保存 コピーする時にメモリを使うので落ちる可能性がある
pickle.dump(copy.deepcopy(model).to_cpu(), open(filename, 'wb'))

#3  破壊的にCPUになっちゃうのでepoch2以降はそのままじゃ続行できないがメモリ使わずCPUからも利用できる
pickle.dump(model.to_cpu(), open(filename, 'wb'))

と一長一短。
GPUはCUDAのコア数なども大事かもしれませんが、メモリが少ないと制約が多すぎて辛いです。お金に余裕がある人は最初からいいやつ買いましょう。

*1:CUDAドライバ本体入れたら一緒に入るのかな?もしかしたら追加でインストールしたのかもしれません