9. 레시피
파이썬 코드 파싱 ast
C언어 변환 cython
컴파일 distutils
모듈 동적 로드 importlib
10. Cython
파이썬 문법을 기반
파이썬 코드를 C 언어로 변환
변환된 소스코드(C 언어)를 컴파일
컴파일된 모듈을 파이썬과 연동
Cython 코드
def integrate_f(double a, double b, int N):
cdef int i
cdef double s, dx
s = 0
dx = (b - a)/N
for i in range(N):
s += f(a + I * dx)
return s * dx
11. Cython
차이점은 변수 타입을 선언
선언하지 않아도 문제 없습니다
변수 타입을 지정하면 속도 증가
Cython 코드
def integrate_f(double a, double b, int N):
cdef int i
cdef double s, dx
s = 0
dx = (b - a)/N
for i in range(N):
s += f(a + I * dx)
return s * dx
13. JIT 컴파일 과정
JIT 함수 선언
JIT 함수 실행
Cython 코드 생성
C 코드 생성
컴파일
모듈 로드
실행
14. JIT 컴파일 과정
JIT 함수 선언
JIT 함수 실행(런타임)
Cython 코드 생성(런타임)
C 코드 생성(런타임)
컴파일(런타임)
모듈 로드(런타임)
실행
15. JIT 함수 선언
@jit
def f(I, J):
res = 0.0
for i in range(I):
for j in range (J * I):
res += 1
return res
데코레이터로 표시
이 함수가 실행되면 JIT 컴파일
16. JIT 함수 호출
result = f(1, 500) 호출 시 인자의 타입 정보를 가지고 JIT 컴파일 시작
1은 정수형
500도 정수형
17. Cython 코드 생성
def f(I, J):
res = 0.0
for i in range(I):
for j in range (J * I):
res += 1
return res
함수 내부의 변수 타입을 찾자(코드를 파싱)
import ast
import inspect
node = ast.parse(inspect.getsource(f))
18. Cython 코드 생성
def f(I, J):
res = 0.0
for i in range(I):
for j in range (J * I):
res += 1
return res
함수 코드의 AST(Abstract Syntax Tree) 추출
19. Cython 코드 생성
def f(I, J):
res = 0.0
for i in range(I):
for j in range (J * I):
res += 1
return res
변수 res 는 실수형
20. Cython 코드 생성
def f(I, J):
res = 0.0
for i in range(I):
for j in range (J * I):
res += 1
return res
변수 i, j 는 정수형
21. Cython 코드 생성
AST를 다시 코드로 변환하여 Cython 코드 생성 def jit_f(I, J):
res = 0.0
for i in range(I):
for j in range(J * I):
res += 1
return res
22. Cython 코드 생성
변수 타입 정보를 가지고 Cython 변수 선언
def jit_f(long I, long J):
cdef long j
cdef long i
cdef double res
res = 0.0
for i in range(I):
for j in range(J * I):
res += 1
return res
23. C 코드 생성
def jit_f(long I, long J):
cdef long j
cdef long i
cdef double res
res = 0.0
for i in range(I):
for j in range(J * I):
res += 1
return res
from Cython.Build.Dependencies import Cythonize
.....
Cythonize(.....)
.....
26. import imp
Cython_module = imp.load_dynamic(name, path)
f = Cython_module.jit_f
모듈 동적 로드
함수 f는 컴파일 된 함수(jit_f)로 교체
27. 실행
result = f(1, 500)
result = jit_f(1, 500)
def jit(func):
def wrapper(*args, **kwargs):
func 코드를 Cython 코드로 변환
Cython 코드, C 코드 컴파일
모듈 동적 로드
return jit_func(*args, **kwargs)
return wrapper
28. 실행
result = f(1, 500)
result = jit_f(1, 500)
컴파일 된 함수 jit_f(1, 500) 실행
1000000000.0 을 반환하여 result 에 저장
만약 또 f 함수가 실행 된다면?
이미 준비 된 jit_f를 바로 실행
29. 성능
소수 찾기
def primes(kmax):
result = list()
p = [0 for i in range(1000)]
k = 0
n = 2
while k < kmax:
i = 0
while i < k and n % p[i] != 0:
i = i + 1
if i == k:
p[k] = n
k = k + 1
result.append(n)
n = n + 1
return result
30. def primes(kmax):
result = list()
p = [0 for i in range(1000)]
k = 0
n = 2
while k < kmax:
i = 0
while i < k and n % p[i] != 0:
i = i + 1
if i == k:
p[k] = n
k = k + 1
result.append(n)
n = n + 1
return result
성능
소수 찾기
2.7 배 증가
31. 성능
예제 코드 def f(I, J):
res = 0.0
for i in range(I):
for j in range (J * I):
res += 1
return res
32. def f(I, J):
res = 0.0
for i in range(I):
for j in range (J * I):
res += 1
return res
성능
예제 코드
36 배 증가
33. 결론
Python 코드를 기계어로 컴파일
컴파일 된 기계어를 로딩하여 실행
같은 코드가 반복 실행되면 캐싱 된 기계어를 직접 실행
위 과정을 런타임에 수행하는 JIT 컴파일러 구현
34. 그러나
간단한 JIT 컴파일러의 제약
함수가 최초로 실행될 때 컴파일 시간이 필요합니다
Python Cython C Binary Run
자주 반복되지 않으면 직접 실행보다 느릴 수 있습니다
자주 반복되는 코드에 효과적
메모리 누수 발생 가능성?! 예외 처리?!
35. 그래도
프로토타입은 Python 코드 LLVM 중간코드 생성
복잡한 과정 없이 단순하게 LLVM 중간코드 생성
성숙된 LLVM 기술 사용 가능
36. LLVM 중간코드 생성
@llvm.jit
def f(I, J):
res = 0.0
for i in range(I):
for j in range (J * I):
res += 1
return res
37. LLVM 중간코드 생성
@llvm.jit
def f(I, J):
res = 0.0
for i in range(I):
for j in range (J * I):
res += 1
return res
38. 마지막으로
LLVM 을 사용하면 효과적인 JIT 컴파일러 제작
LLVM 을 설명하기에는 여백이 부족…
휼륭한 LLVM 기반 JIT 컴파일러가 존재?!
https://numba.pydata.org/