[Cython][Python] 고성능 Python을 위한 Cython 활용하기 2편

안녕하세요. Teus입니다.

 

지난 포스팅을 통해서 Cython을 사용하는 방법과 Cython이 어떻게 이뤄졌는지 확인 했습니다.

하지만, Python의 List와 같은 Array 데이터를 활용하는데 문제점을 확인 했습니다.

그래서

이 문제점을 해결하기 위해서 numpy Array를 Cython에서 활용하는 방법을 알아보도록 하겠습니다.

 

출처 : Cython numpy 활용 공식문서

 

Cython에서 Numpy 사용하기

cython에서 numpy를 사용하기 위해서는 .pyx 파일에서 역시 numpy를 import해야 합니다.

하지만 이때 cython은 독특하게

아래처럼 여러가지 부가작업이 필요합니다.

%%cython
#cython에서 numpy를 사용하기 위해서 아래 3개를 import 해야됨
import numpy as np
cimport numpy as np
np.import_array()
def np_foo(object f):
    print(f)
    
temp = np.array([i for i in range(100)])
np_foo(temp)

이때 numpy의 add, mul연산 처럼 CPython Level로 정의된 연산의 경우

 

cython을 사용하나, python에서 실행하나 큰 차이가 없습니다.

%%cython
import numpy as np
cimport numpy as np
np.import_array()
def cython_np_add(np.ndarray f):
    for _ in range(10000):
        f += 5    
    return f

def python_np_add(f):
    for _ in range(10000):
        f += 5
    return f

import time
temp = np.array([i for i in range(4000000)])

st = time.time()
cython_np_add(temp)
print(f"cython time : {time.time()-st}")
st = time.time()
python_np_add(temp)
print(f"python time : {time.time()-st}")
'''
cython time : 16.287737607955933
python time : 16.263918161392212
'''

하지만 cython을 통해서 numpy array의 데이터에 index로 접근할 경우 결과가 달라지게 됩니다.

%%cython
import numpy as np
cimport numpy as np
np.import_array()
def cython_reduce_sum_with_numpy(np.ndarray f):
    cdef long long i = 0;
    cdef long long ret = 0;
    cdef long long data_len = f.shape[0];
    for i in range(data_len):
        ret += f[i]   
    return ret

def python_reduce_sum_with_numpy(f):
    ret = 0
    for i in range(len(f)):
        ret += f[i]
    return ret

import time
temp = np.array([i for i in range(100000000)])

st = time.time()
cython_reduce_sum_with_numpy(temp)
print(f"cython time : {time.time()-st}")
st = time.time()
python_reduce_sum_with_numpy(temp)
print(f"python time : {time.time()-st}")
"""
cython time : 8.304328441619873
python time : 4.437905550003052
"""

image.png

Cython을 사용한게 오히려 Python보다 두배는 느려지는 결과를 얻게 됩니다.

 

Cython에서 Numpy 최적화 하기1

cython의 경우 numpy array의 Data Type과 ndim을 입력하지 않을 경우 [] operator를 통한 indexing이 Python Operation으로 처리되게 됩니다.

때문에, Cython과 Python Operation이 번갈아 가면서 오히려 성능이 저하됩니다.

이때 공식문서의 안내에 따라 np.ndarray의 Data Type과 ndim을 입력할 경우 놀라운 속도 향상을 확인할 수가 있습니다.

%%cython
import numpy as np
cimport numpy as np
np.import_array()
#cython의 매개변수에서 활용하기 위한 ctypedef
ctypedef np.int_t DTYPE_t

#매개변수로 받은 np.ndarray의 Data Type과 ndim을 명기해줌
def cython_reduce_sum_with_numpy(np.ndarray[DTYPE_t, ndim = 1] f):
    cdef long long i = 0;
    cdef long long ret = 0;
    cdef long long data_len = f.shape[0];
    for i in range(data_len):
        ret += f[i]   
    return ret

def python_reduce_sum_with_numpy(f):
    ret = 0
    for i in range(len(f)):
        ret += f[i]
    return ret

import time
temp = np.array([2 for i in range(100000000)])

st = time.time()
cython_reduce_sum_with_numpy(temp)
print(f"cython time : {time.time()-st}")
st = time.time()
python_reduce_sum_with_numpy(temp)
print(f"python time : {time.time()-st}")
'''
cython time : 0.07773971557617188
python time : 3.8791370391845703
'''

위 두 코드를 실행하며, annotate를 추가하여 확인하면 Data Type과 ndim을 명시한 뒤에 python operation이 줄어드는 것을 확인할 수가 있습니다.

(add과정에서 Py_GetItem이 사용되며, 주기적으로 변수의 Reference Counter를 조정하는것을 볼 수 있습니다)

 

<Data Type과 ndim을 기입하지 않은 경우>

image.png

 

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

<Data Type과 ndim을 기입한 경우>

image.png

 

Cython에서 Numpy 최적화 하기2

여기서 추가적인 성능 향상이 가능합니다.

공식문서에서는 Decorator를 통해서 반복문의 bound check와 minus index에대한 고려사항을 제외시킬 수가 있습니다.

아래 코드를 보겠습니다.

%%cython --annotate
import numpy as np
cimport numpy as np
np.import_array()
DTYPE = np.int_
ctypedef np.int_t DTYPE_t

cimport cython
@cython.boundscheck(False) # turn off bounds-checking for entire function
@cython.wraparound(False)  # turn off negative index wrapping for entire function
def cython_reduce_sum_with_opt_numpy(np.ndarray[DTYPE_t, ndim = 1] f):
    cdef long long i = 0;
    cdef long long ret = 0;
    cdef long long data_len = f.shape[0];
    for i in range(data_len):
        ret += f[i]   
    return ret

def cython_reduce_sum_with_numpy(np.ndarray[DTYPE_t, ndim = 1] f):
    cdef long long i = 0;
    cdef long long ret = 0;
    cdef long long data_len = f.shape[0];
    for i in range(data_len):
        ret += f[i]   
    return ret

import time
temp = np.array([2 for i in range(100000000)])

st = time.time()
cython_reduce_sum_with_numpy(temp)
print(f"cython time : {time.time()-st}")
st = time.time()
cython_reduce_sum_with_opt_numpy(temp)
print(f"cython opt time : {time.time()-st}")
'''
cython time : 0.0887763500213623
cython opt time : 0.03389263153076172
'''

코드 실행 결과를 보면 최적화 전 대비 2배 이상 빠른속도를 보여줍니다.

이 차이점은 annotate를 통해서 바로 확인할 수가 있습니다.

 

image.png

CPython으로 변환된 코드를 보면

boundcheck와 minus index를 고려하지 않게 되면서 더이상 Python Operation이 없이 동작하는것을 알 수가 있습니다.

덕분에 python에서 경험할 수 없는 빠른 속도로 결과를 얻어낼 수가 있습니다.

(단, bound check가 사라지기 때문에 데이터 오염 또는 프로그램 crash가 일어날 수가 있습니다)

 

더 빠른 Python를 위해서

Cython 1, 2편을 통해서 어떻게 Python의 데이터를 Cython 함수에 보내고, 활용할 수 있는지 확인하였습니다.

다음 포스팅에서는 Cython 함수에서 Numpy Array를 받고

Array를 Python의 GIL없이 멀티쓰레드를 사용하여 처리하는 방법에 대해서 다루도록 하겠습니다.

(다음편이 Cython 연재 마지막 입니다!)

 

감사합니다!

 

[출처] https://devocean.sk.com/blog/techBoardDetail.do?ID=164605

 

 

 

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
17 [python 수학] FizzBuzz를 '개발자답게' 구현해보자 file 졸리운_곰 2024.12.26 317
16 [python 수학] matplot ylim How to set the axis limits y축 범위 고정 졸리운_곰 2024.06.08 434
15 [python 수학] [PYTHON] bar 그래프에 백분율 표시하기 file 졸리운_곰 2024.06.08 476
14 [python 수학] [Python] 막대 그래프 (Bar Chart) file 졸리운_곰 2024.06.08 357
13 [Python 수학] Plotting With PyQtGraph 졸리운_곰 2024.06.07 558
12 [Python 수학] 그래프 라이브러리 PyQtGraph 2D Graph 예제 코드 file 졸리운_곰 2024.06.06 498
11 [python 수학] [PYTHON] bar 그래프에 백분율 표시하기 file 졸리운_곰 2024.06.06 721
10 [python 수학] [Numpy] 넘파이 기본 문법 정리 졸리운_곰 2023.11.28 605
9 [python 수학] Numpy 많이쓰는 함수 정리 졸리운_곰 2023.11.28 533
8 [Python 수학] Python/데이터 사이언스 [파이썬] Numpy 정리 졸리운_곰 2023.11.28 428
7 [python][anaconda] 파이썬3(python3) 설치하고 환경(env) 관리하기 - 아나콘다3(anaconda3)를 활용한 설치 file 졸리운_곰 2022.01.20 358
6 [python][anaconda] 파이선 아나콘다 최신 버전 업데이트하기 file 졸리운_곰 2022.01.20 714
5 [python] 시험삼아 만들어본 로또 번호 생성기 졸리운_곰 2017.02.28 2214
4 Introduction to Python for Econometrics_Statistics and Data Analysis.pdf file 졸리운_곰 2016.06.07 2529
3 Numerical.Methods.in.Engineering.with.Python.2nd.Edition.Jaan.Kiusalaas.2010.pdf file 졸리운_곰 2016.06.07 2499
2 NumMethodPython.pdf file 졸리운_곰 2016.06.07 2419
1 Python-for-Computational-Science-and-Engineering.pdf file 졸리운_곰 2016.06.07 2316
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED