평범한 python 구문으로는 이렇게 처리되는 것을,
if a > 10:
b = 10
elif a > 5:
b = 20
elif a > 0:
b = 0
else:
b = -1
numpy 에서는 이렇게 처리해야 한다.
b = numpy.where(
a > 10,
10,
numpy.where(
a > 5,
20,
numpy.where(
a > 0,
0,
-1
)
)
)
속도 희생을 감수하고서라도, numpy.vectorize를 사용하면 다음과 같이 쓸 수 있다.
def func(x):
if x > 10:
return 10
elif a > 5:
return 20
elif a > 0:
return 0
else:
return -1
vfunc = numpy.vectorize(func)
b = vfunc(a)
첫 번째 코드 조각은 모든 격자에 대해 각각 계산하는 구문이다.(모든 격자를 순회하는 외부 반복문은 생략되었음)
두 번째 코드 조각은 객체에 대해 직접 계산하는 구문이다.(a, b는 2차원 numpy 배열로서 각종 연산자가 오버로딩 되어 있음)
더욱이 아래와 같은 처리를 하고자 할 때, numpy.where 구문으로의 표현은 훨씬 복잡해 진다.
c = 0
if a > 10:
b = 10
elif a > 5:
b = 20
elif a > 0:
b = 0
else:
b = -1
c = -1
numpy.array 를 이용하면서 얻을 수 있는 장점은 많다. 가장 큰 두 가지 장점은 pythonic(유지보수, 확장성 등 python 에 관한 거의 모든 것)과 처리속도(cython, C/C++ 확장 등을 동원해도 numpy 의 속도를 넘지는 못했다. 단 싱글 스레드에서)이다.
numpy.array 의 각 원소(래스터 자료를 다루고 있으므로 '격자')를 cell by cell 로 접근하여 처리해보았는데, 어머어마하게 느린 속도에 깜짝놀랐다. numpy 의 속도에 근접하기 위해서는 여러 가지 단점을 감내한 채로 cython 을 사용해야 하는 것이 그나마 나은 선택이라는 것을 알았지만, 역시 처리 속도는 두 배이상 차이가 났다.
테스트에 사용된 연산식은 보통 많이 사용되는 몇 가지를 골라 임의로 작성하였다.
sin(a)*cos(a)*tan(a)*sqrt(a)*exp(a)*sin(b)*cos(b)*tan(b)*sqrt(b)*exp(b)
a, b 모두 4000 x 4000 크기의 임의의 값으로 채워넣고 위 연산을 적용해본 결과 numpy 는 최고 1.2s(ㅎㄷㄷ) 가 기록되었고 cython 을 최대한(?) 이용한 결과는 최고 2.1s(ㅠ.ㅠ)가 기록되었다. 순수 파이선 반복으로는 183s(@.@)가 기록되었다.
cython 을 사용하면서 발생하는 다양한 불편한 점을 감수하고라도 뭇 프로그래밍 언어가 사용하는 처리 기법(위 예제에서 numpy.where 가 아닌 if 문)을 사용하는 것이 더 나은게 아닌가 싶기도하다.
cython-parallel(cython & openmp) 조합으로 만족할 만한 수준으로 속도를 끌어올릴 수 있었으나, CPU의 모든 코어를 점유하면서까지 이 정도의 속도 향상이 과연 적당한지 의문이다.
E5-2690 V4 x 2ea (28코어 56스레드)로 구동해본 결과 numpy(싱글스레드)는 1.9s, 나름 최적화한 cython&openmp 로는 0.19s 정도가 기록되었다. 56개의 스래드를 전개하고도 10배 향상 밖에 없으니, 뭔가 손해보는 느낌도 있고, 더 다듬어야할 여지가 있는것 같기도 하다.
#01. Shape: 5000x5000 #02. algebra_python * elapsed time: 1.9640274047851562 #03. algebra_python_brute_force * elapsed time: 282.20939111709595 * isclose: True #04. algebra_cython_optimize_0 * elapsed time: 12.367467880249023 * isclose: True #05. algebra_cython_optimize_1 * elapsed time: 3.5004560947418213 * isclose: True #06. algebra_cython_optimize_2 * elapsed time: 3.542889356613159 * isclose: True #07. algebra_cython_optimize_3 * elapsed time: 1.7143189907073975 * isclose: True #08. algebra_cython_optimize_4 * elapsed time: 0.2050466537475586 * isclose: True #09. algebra_cython_optimize_5 * elapsed time: 0.19355154037475586 * isclose: True