[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

 

 

 

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
64 [python][자료구조] "pdf 취소선 검출" How to identify strike-out text from PDF files using Python 졸리운_곰 2024.07.18 544
63 [python][자료구조] 오픈 소스 Python PDF 파서 라이브러리 file 졸리운_곰 2024.07.16 444
62 [python][자료구조] 벡터화 | Vectorization file 졸리운_곰 2023.12.21 330
61 [python][자료구조] Python의 Loop? NO! , Python의 Vectorization? OK!!! file 졸리운_곰 2023.12.21 473
60 [python][자료구조] Python - MySQL 데이터 추가, 삭제, 업데이트 졸리운_곰 2023.05.13 488
59 [python][자료구조] Python의 JSON - 문자열을 JSON으로 변환하는 방법 졸리운_곰 2023.05.06 456
58 [python][자료구조] [Python] Logging to MongoDB (로그 남기기) file 졸리운_곰 2023.03.24 428
57 [python][자료구조] [스파르타 웹 개발 종합] 파이썬으로 크롤링하고 DB에 저장하기(request, bs4, mongoDB 패키지 사용) 졸리운_곰 2023.03.12 461
56 [python][자료구조] [MongoDB] Document Query(조회) – find() 메소드 졸리운_곰 2023.03.08 315
55 [python][자료구조] Python(flask)으로 mongoDB 제어하기 – pymongo file 졸리운_곰 2023.03.08 604
54 [python][자료구조] django sqlite3 MySQL로 전환하기 file 졸리운_곰 2023.03.02 409
53 [python][자료구조] django에서 db.sqlite3 데이터를 mysql로 옮기기 졸리운_곰 2023.03.02 427
52 [python][자료구조] Python - JSON 파일 읽고 쓰는 방법 졸리운_곰 2023.02.04 480
51 [python][자료구조] [인코딩] 유니코드 인코딩 처리 (특히 json 입출력 시) 졸리운_곰 2023.02.04 363
50 [python][자료구조] Dropbox API 사용하기 (with python) 졸리운_곰 2022.12.03 306
49 [Python][자료구조] SQLAlchemy Tutorial(한글) - 2 졸리운_곰 2022.12.03 393
48 [Python]][자료구조] SQLAlchemy Tutorial(한글) - 1 졸리운_곰 2022.12.03 275
47 [python][자료구조] python anaconda 에서 mysql 접속 졸리운_곰 2022.01.25 657
46 [python 자료구조] 림코딩의 파이썬으로 csv 다루기 강좌 (읽기,쓰기,수정,추가) 졸리운_곰 2022.01.16 521
45 python - 읽은 후 kafka 메시지를 삭제하는 방법 졸리운_곰 2021.07.13 900
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED