リスト8-1にNumPyとCuPyを用いて連立一次方程式を解くソースコードを示します。
NumPyはCPU用、CuPyはGPU用です。
どちらもlinalg.solve関数を使用しています。
実数と複素数、単精度と倍精度を選択することができます。
行列は乱数で作成した非対称密行列です。
CuPyではデバイスメモリーを確実に解放するためにmemory poolを使用しています。
リスト8-1 NumPyとCuPyを用いて連立一次方程式を解くソースコード
(leq.py)
"""
leq.py
solve linear equations (NumPy and CuPy)
"""
import numpy as np
import cupy as cp
import time
# parameters
N = 5000 # matrix size
L = 1 # repeat
dkind = 1 # 1=real, 2=complex
precision = 1 # 1=single, 2=double
gpu = 0 # 0=CPU(NumPy), 1=GPU(CuPy)
# dtype
f_dtype = 'f4' if precision == 1 else 'f8'
c_dtype = 'c8' if precision == 1 else 'c16'
# timer
t0 = time.time()
# real
if dkind == 1:
a = np.random.rand(N, N).astype(f_dtype)
b = np.random.rand(N).astype(f_dtype)
x = np.zeros(N, f_dtype)
# complex
elif dkind == 2:
a = np.random.rand(N, N).astype(f_dtype) \
+ np.random.rand(N, N).astype(f_dtype) * 1j
b = np.random.rand(N).astype(f_dtype) \
+ np.random.rand(N).astype(f_dtype) * 1j
x = np.zeros(N, c_dtype)
# alloc device memory
if gpu == 1:
mempool = cp.get_default_memory_pool()
# copy to device
d_a = cp.array(a)
d_b = cp.array(b)
d_x = cp.array(x)
# timer
t1 = time.time()
# solve
for _ in range(L):
if not gpu:
# CPU (NumPy)
x = np.linalg.solve(a, b)
else:
# GPU (CuPy)
d_x = cp.linalg.solve(d_a, d_b)
# timer
t2 = time.time()
# free device memory
if gpu == 1:
# copy to host
x = cp.asnumpy(d_x)
# free
d_a = None
d_b = None
d_x = None
mempool.free_all_blocks()
# check
res = np.dot(a, x) - b
ans = np.linalg.norm(res)
# output
device = 'CPU' if gpu == 0 else 'GPU'
print('(%s) N=%s, %.2f+%.2f[sec], err=%.3e'
% (device, N, t1 - t0, (t2 - t1) / L, ans))
# free
a = None
b = None
x = None
表8-1に実数の連立一次方程式の計算時間を示します。
計算時間を正確に測定するためにN=5000で50回、N=10000で10回計算して回数で割っています。
| 行列の大きさN | CPU (NumPy) | GPU (CuPy) | ||
|---|---|---|---|---|
| 単精度 | 倍精度 | 単精度 | 倍精度 | |
| 5000 | 0.57秒 | 0.52秒 | 0.05秒 | 0.45秒 |
| 10000 | 3.53秒 | 3.49秒 | 0.27秒 | 3.14秒 |
| 20000 | 26.65秒 | 23.65秒 | 2.06秒 | 25.82秒 |
表8-1から以下のことがわかります。
以上から連立一次方程式を解くには、計算時間の観点からはCuPyの単精度が優れていますが、
残差を確認するとCuPyの単精度はNumPyの単精度より2桁大きくなっており、
CuPyの単精度を使用するときは計算精度に注意が必要です。
表8-2に複素数の連立一次方程式の計算時間を示します。
表8-1と同じ傾向が見られます。
複素数の計算時間は実数の4~10倍になります。
| 行列の大きさN | CPU (NumPy) | GPU (CuPy) | ||
|---|---|---|---|---|
| 単精度 | 倍精度 | 単精度 | 倍精度 | |
| 5000 | 2.05秒 | 1.96秒 | 0.11秒 | 1.62秒 |
| 10000 | 14.54秒 | 14.42秒 | 0.70秒 | 12.03秒 |
| 20000 | 206.15秒 | 233.18秒 | 19.91秒 | メモリー不足 |