Cupyを使ってみる

Pythonでの計算はnumpyを使って行列計算を行うと非常に早く実行できますが、それでも桁が大きくなってくるとそれなりに待たされてしまいます。
GPUを使った行列計算はCPUを使ったものよりも、劇的に早くなる可能性を秘めているとの事ですが、なかなか実装が難しそう。
そんな中、CupyというNumpyと互換性のあるライブラリがChainerの一部として存在するそうです。
とても簡単に実行できるのだそう。やってみようやってみよう。

Cupyのサイトはこちら

Cupyのインストールはpipで行えるそう。

pip install cupy-cuda91

特に問題なくインストールも完了。
cuda[xx]の部分はバージョンを入れるようです。
ちなみにcudaのバージョンを調べるのは

nvcc -V

で出来ましたよ。

nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2017 NVIDIA Corporation
Built on Fri_Nov__3_21:07:56_CDT_2017
Cuda compilation tools, release 9.1, V9.1.85

私の環境では9.1ですね。
それではCupyを使って計算速度を測ってみましょう。
ちなみに私のPCの環境は、

CPU : Intel Core i7 6800K 6コア 12スレッド 3.4GHz
GPU : GeForce GTX 1080Ti 11GB
メモリー : 32GB DDR4-2400
ストレージ : 480GB SSD
OS : CentOS 7.4

となっておりました。いろいろ使えそう。

ちなみにそれぞれのコマンドラインからの調べ方は
CPCの種類の確認 :

cat /proc/cpuinfo

GPUの種類の確認 :

lspci | grep -i VGA

メモリーの確認 :

cat /proc/meminfo

OS :

cat /etc/redhat-release

で調べられます。すぐ忘れるのでメモ必須です。

では早速Cupyを試しましょう。サイトにある例を少しもじります。
10000 x 10000の行列をfloatで作成し、内積を取ります。
timeを使って時間を測り、時間を出力します。
まずはnumpyから。

import numpy as np
import time
N_size = 10000
x = np.random.rand(N_size, N_size).astype('f4')
y = np.random.rand(N_size, N_size).astype('f4')

start = time.time()
np.dot(x, y)
elapsed_time = time.time() - start
print("numpy :{0}".format(elapsed_time))

test.pyと名前つけて保存して実行。

python test.py
numpy :4.0388100147247314

10000 x 10000なのでそれなりにかかりますよね。

ではこれをCupyに変えてみます。

import cupy as cp
import time
N_size = 10000
x = cp.random.rand(N_size, N_size).astype('f4')
y = cp.random.rand(N_size, N_size).astype('f4')

start = time.time()
cp.dot(x, y)
elapsed_time = time.time() - start
print("cupy :{0}".format(elapsed_time))

ほんとにそのままですね。これを保存して実行。

python test.py
cupy :0.1345818042755127

うん、早くなった。
更に、numpyで用意していた変数をGPUに移すには以下のようにするらしいです。

import numpy as np
import cupy as cp
import time
N_size = 10000
x = np.random.rand(N_size, N_size).astype('f4')
y = np.random.rand(N_size, N_size).astype('f4')

x_cp = cp.asarray(x)
y_cp = cp.asarray(y)

start = time.time()
cp.dot(x_cp, y_cp)
elapsed_time = time.time() - start
print("cupy :{0}".format(elapsed_time))
python test.py
cupy :0.17929410934448242

あれ?さっきより遅くなった。そういう、、、ものなのかな?