プログラムが遅いと感じたらどうする? 「よし、とりあえずforループを全部FortranやCに書き換えて・・・」
↑は間違いです! まずはボトルネックを調べるべき
もし全てのコードを高速化しようとしたら Pythonの利点であるコーディング時間の短さを失う!
ボトルネックの種類を見極める
(例)成分ごとにforループを回している
高速化のためのライブラリが存在する
Numba Cython ctypes ↑に共通するのはコンパイルするということ!
例えば
Numpyは並のC言語のコードよりも速い この場合、CやFortranに書き換えたとしても劇的に早くなることはない
「Python”だから”遅い」ときの対処法 どのくらい遅いか(sumの場合)
import numpy as np
# 1000×1000の二次元配列
A = np.random.randn(1000, 1000)
%timeit A.sum()
def my_sum(arr2d):
I = arr2d.shape[0]
J = arr2d.shape[1]
res = 0.
for i in range(I):
for j in range(J):
res += arr2d[i, j]
return res
%timeit my_sum(A)
2桁ぐらい遅い!
Numbaは、JIT(Just In Time)コンパイラ 初めての実行時にコンパイルを行い、それ以降はコンパイルされたものを使う。
(Just In Time = 必要になるときに必要な分だけ)
from numba import jit
import numba as nb
@jit
def my_sum_Numba(arr2d):
I = arr2d.shape[0]
J = arr2d.shape[1]
res = 0.
for i in range(I):
for j in range(J):
res += arr2d[i, j]
return res
# 実行時に初めてコンパイル
my_sum_Numba(np.random.randn(*A.shape))
%timeit my_sum_Numba(A)
Numpyのsum関数より少し遅いが、生Pythonよりは100倍以上速い
A = np.random.randn(100, 30)
B = np.random.randn(30, 1000)
C = np.empty((1000, 1000))
%timeit np.dot(A, B)
def my_mul(A, B):
M_A, N = A.shape
N_B = B.shape[1]
C = np.zeros((M_A, N_B))
for i in range(M_A):
for j in range(N_B):
for k in range(N):
C[i, j] += A[i, k] * B[k, j]
return C
%timeit my_mul(A, B)
数桁遅い
# @jit(nb.float64[:,:](nb.float64[:,:], nb.float64[:,:]), nopython=True)
@jit
def my_mul_numba(A, B):
M_A, N = A.shape
_, N_B = B.shape
C = np.zeros((M_A, N_B))
for i in range(M_A):
for j in range(N_B):
for k in range(N):
C[i, j] += A[i, k] * B[k, j]
return C
my_mul_numba(A, B)
%timeit my_mul_numba(A, B)