CuPyはNumPyとSciPyで記述されたCPU用のプログラムをGPUで動かすためのツールです。
簡単な移植作業により、GPUの高い計算性能を生かして高速に計算することができます。
リスト5-1にCuPyを用いてベクトル和とベクトル内積を計算するプログラムを示します。
計算時間を正確に測定するためにEventクラスを使用しています。
リスト5-1 CuPyによるベクトル和のベクトル内積のソースコード
(vector_cupy.py)
"""
vector_cupy.py
test program of vector operations
CuPy
"""
import numpy as np
import cupy as cp
# (vadd) add two vectors : CuPy + NumPy version
def vadd(a, b):
return a + b
# (sdot-1) scalar product of two vectors : CuPy version
def sdot_cupy(a, b):
return cp.dot(a, b)
# (sdot-2) scalar product of two vectors : NumPy version
def sdot_numpy(a, b):
return np.dot(a, b)
# parameters
N = 100000000
L = 100
dtype = 'f4' # 'f4' or 'f8'
fn = 'vadd-1'
#fn = 'sdot-1'
# timer
start = cp.cuda.Event()
end = cp.cuda.Event()
start.record()
# setup
a = np.arange(N).astype(dtype)
b = np.arange(N).astype(dtype)
if fn.startswith('vadd'):
c = np.zeros(N, dtype)
# copy to device
d_a = cp.array(a)
d_b = cp.array(b)
if fn.startswith('vadd'):
d_c = cp.array(c)
# timer
end.record()
end.synchronize()
t1 = cp.cuda.get_elapsed_time(start, end)
start.record()
# calculation
for _ in range(L):
if fn == 'vadd-1':
d_c = vadd(d_a, d_b)
elif fn == 'vadd-2':
c = vadd(a, b)
elif fn == 'sdot-1':
s = sdot_cupy(d_a, d_b)
elif fn == 'sdot-2':
s = sdot_numpy(a, b)
# timer
end.record()
end.synchronize()
t2 = cp.cuda.get_elapsed_time(start, end)
# check
if fn == 'vadd-1':
c = cp.asnumpy(d_c)
r1 = np.sum(c)
r2 = N * (N - 1)
elif fn == 'vadd-2':
r1 = np.sum(c)
r2 = N * (N - 1)
else:
r1 = s
r2 = N * (N - 1) * (2 * N - 1) / 6
# output
print('(%s) N=%d, L=%d' % (fn, N, L))
print('%.2f+%.2f[sec], %e, %e' % (t1 * 1e-3, t2 * 1e-3, r1, r2))
# free
a = None
b = None
d_a = None
d_b = None
if fn.startswith('vadd'):
c = None
d_c = None
CuPyのNumPyとの主な違いは以下の通りです。
表5-1にCuPyのベクトル和とベクトル内積の計算時間を示します。
配列の大きさ(=N)と繰り返し回数(=L)の積は一定(=1010)です。
従って全体の演算量は同じです。
| No. | 配列の大きさN | 繰り返し回数L | ベクトル和 | ベクトル内積 | ||
|---|---|---|---|---|---|---|
| 単精度 | 倍精度 | 単精度 | 倍精度 | |||
| 1 | 100,000,000 | 100 | 0.49秒 | 1.00秒 | 0.65秒 | 1.30秒 |
| 2 | 10,000,000 | 1,000 | 0.50秒 | 0.99秒 | 0.66秒 | 1.33秒 |
| 3 | 1,000,000 | 10,000 | 0.52秒 | 1.03秒 | 0.94秒 | 1.44秒 |
| 4 | 100,000 | 100,000 | 2.34秒 | 2.49秒 | 9.78秒 | 9.90秒 |
| 5 | 10,000 | 1,000,000 | 28.48秒 | 27.34秒 | 99.60秒 | 98.78秒 |
表5-1から以下のことがわかります。