numba의 guvectorize를 사용하면 array 형태의 값 처리의 속도를 매우 빠르게 할 수 있다.
우선 실행 결과를 먼저 보겠다. aaa는 guvectorize를 사용해서 연산한 경우이고, bbb는 그냥 python 코드로 실행한 결과이다. ccc는 numpy의 벡터 연산을 실시한 결과다. guvectorize를 적용했을 때 단순 for loop를 사용했을 때에 비해 약 300배 가까이 빠른 실행을 보였다. numpy의 벡터 연산을 활용하면 for loop을 사용했을 때 보다 1400배 가까이 빠르게 실행되었다.
aaa: elapsed=0.0275056362s
bbb: elapsed=9.2325780392s
ccc: elapsed=0.0066127777s
>>> 9.2325780392 / 0.0275056362
335.6613158142476
>>> 9.2325780392 / 0.0066127777
1396.1724494685493
결론부터 말하자면, Array의 단순 연산은 numpy의 벡터연산이 가장 빠르다. 특정 로직에 따른 연산이 필요할 땐 guvectorize를 활용하자.
guvectorize의 사용법을 간단히 살펴보자. guvectorize의 경우에는 값을 return하지 않는다. 대신에 파라미터에 대한 선언을 정확히 해 주면 된다.
@guvectorize(['void(int64[:], int64[:])'], '(n) -> (n)')
def function_name(x, y):
순서대로 int64[:], int64[:]는 뒤의 (n) -> (n)과 매칭된다.
Array가 아닌 단순 값을 파라미터로 추가하는 경우에는 아래와 같이 처리하면 된다. 데이터 타입의 정의 개수와 입력, 출력 파라미터의 형식, 그리고 함수의 정의까지 모두 일치해야 한다.
@guvectorize(['void(int64[:], boolean, int64[:])'], '(n),() -> (n)')
def function_name(x, twist, y):
아래에는 위의 결과를 만든 소스 코드를 붙인다.
import time
import numpy as np
from numba import guvectorize
def timer(func):
def measure(*args, **kwargs):
begin = time.time()
val = func(*args, **kwargs)
end = time.time()
print(f">> {func.__name__}: elapsed={'%.10f' % (end - begin)}s")
return val
return measure
@timer
@guvectorize(['void(int64[:], int64[:])'], '(n) -> (n)')
def aaa(x, y):
for i, val in enumerate(x):
y[i] = val + 3 * 3 / 5 * 1.23 / 6.53
@timer
def bbb(x):
y = np.empty([len(x), 1])
for i, val in enumerate(x):
y[i] = (val + 3 * 3 / 5 * 1.23 / 6.53)
return y
@timer
def ccc(x):
return x + 3 * 3 / 5 * 1.23 / 6.53
a = np.int32(np.round(np.random.rand(5000000) * 1000))
out = aaa(a)
out = bbb(a)
out = ccc(a)