Numba for CUDA GPUs を用いると
NVIDIA社のグラフィックスボード(GPU)を用いて高速に計算することができます。
C版のCUDAとほぼ同様のプログラムになります。
リスト6-1にベクトル和とベクトル内積を Numba for CUDA で計算するプログラムを示します。
ベクトル内積についはベクトル同士の積(アダマール積)を計算した後、
reduction(@cuda.reduce を用いる)によって和を計算しています。
計算時間を正確に測定するためにeventを使用しています。
リスト6-1 Numba for CUDAによるベクトル和とベクトル内積のソースコード
(vector_cuda.py)
"""
vector_cuda.py
test program of vector operations
Numba for CUDA
"""
import numpy as np
from numba import cuda
# (1) add two vectors: c = a + b
@cuda.jit(cache=True)
def vadd(a, b, c):
tid = cuda.grid(1) # = threadIdx.x + (blockIdx.x * blockDim.x)
n = len(c)
if tid < n:
c[tid] = a[tid] + b[tid]
# (2) scalar product of two vectors (a . b)
def sdot(a, b, c, nblocks, nthreads):
vmul[nblocks, nthreads](a, b, c)
return vsum(c)
# multiplicate two vectors: c = a * b
@cuda.jit(cache=True)
def vmul(a, b, c):
tid = cuda.grid(1)
n = len(c)
if tid < n:
c[tid] = a[tid] * b[tid]
# sum of a vector (reduction)
@cuda.reduce
def vsum(a, b):
return a + b
# parameters
N = 100000000
L = 100
dtype = 'f4' # 'f4' or 'f8'
fn = 'vadd'
#fn = 'sdot'
# timer
start = cuda.event()
end = cuda.event()
start.record()
# host memory
a = np.arange(N).astype(dtype)
b = np.arange(N).astype(dtype)
# device memory
d_a = cuda.to_device(a)
d_b = cuda.to_device(b)
d_c = cuda.device_array_like(d_a)
# Execution Configuration
nthreads = 256
nblocks = (N + (nthreads - 1)) // nthreads
# timer
end.record()
end.synchronize()
t1 = cuda.event_elapsed_time(start, end)
start.record()
# calculation
for _ in range(L):
if fn == 'vadd':
vadd[nblocks, nthreads](d_a, d_b, d_c)
elif fn == 'sdot':
s = sdot(d_a, d_b, d_c, nblocks, nthreads)
# timer
end.record()
end.synchronize()
t2 = cuda.event_elapsed_time(start, end)
# output
if fn == 'vadd':
s = np.sum(d_c.copy_to_host())
exact = N * (N - 1)
elif fn == 'sdot':
exact = N * (N - 1) * (2 * N - 1) / 6
print('N=%d, L=%d' % (N, L))
print('%.2f+%.2f[sec], %e, %e' % (t1 * 1e-3, t2 * 1e-3, s, exact))
# free
a = None
b = None
d_a = None
d_b = None
d_c = None
Numba for CUDA のC版のCUDAとの主な違いは以下の通りです。
表6-1に Numba for CUDA によるベクトル和とベクトル内積の計算時間を示します。
配列の大きさ(=N)と繰り返し回数(=L)の積は一定(=1010)です。
従って全体の演算量は同じです。
| No. | 配列の大きさN | 繰り返し回数L | ベクトル和 | ベクトル内積 | ||
|---|---|---|---|---|---|---|
| 単精度 | 倍精度 | 単精度 | 倍精度 | |||
| 1 | 100,000,000 | 100 | 0.57秒 | 1.15秒 | 1.29秒 | 1.98秒 |
| 2 | 10,000,000 | 1,000 | 0.52秒 | 1.02秒 | 1.29秒 | 2.02秒 |
| 3 | 1,000,000 | 10,000 | 0.61秒 | 1.02秒 | 7.30秒 | 7.08秒 |
| 4 | 100,000 | 100,000 | 6.54秒 | 6.72秒 | 66.30秒 | 66.63秒 |
| 5 | 10,000 | 1,000,000 | 67.34秒 | 66.78秒 | 660秒(注1) | 660秒(注1) |
(注1)繰り返し回数Lが1/10のときの計算時間を10倍した推定値です。
表6-1から以下のことがわかります。