티스토리 뷰

728x90
반응형

안녕하세요. Teus입니다.

이번 포스팅은, what's new in python3.13 에서 새롭게 소개된 실험적 기능 중 하나인

python jit compiler를 빌드하는 방법을 소개하고, 간단하게 일반 python과의 성능 차이에 대해서 알아봅니다.

0. JIT

PEP 744 – JIT Compilation
GIL때문에 관심을 전혀 받지 못하고 있었지만,

Python자체적으로 JIT(Just In Time) Compiler가 준비되고 있었고

해당 기능이 이제 Experimental Feature로 사용이 가능하게 되었습니다.

Python의 Jit같은 경우, “copy-and-patch” 기술을 사용해서 High-level로 작성된 코드의 byte code 최적화를 가능하게 해줍니다.
혹시 copy-and-patch 기술이 궁금하실 경우, 아래 내용들을 참고하시면 됩니다.
https://dl.acm.org/doi/10.1145/3485513
https://www.youtube.com/watch?v=HxSHIpEQRjs&feature=youtu.be
https://sillycross.github.io/2023/05/12/2023-05-12/

1. jit Compiler 빌드하기

먼저 python 3.13.0 버전 소스파일일을 curl을 통해서 받아옵니다.

export PYTHON_VERSION=3.13.0
export PYTHON_MAJOR=3
curl -O https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz

이때 PYTHON JIT같은경우, llvm컴파일러를 요구합니다.

컴파일러 환경 및 llvm 18, 19 를 설치해 줍니다

sudo apt-get install build-essential gcc zlib1g-dev
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 18
sudo ./llvm.sh 19

https://github.com/python/cpython/blob/main/Tools/jit/README.md
설치 official Readme에서는 단순하게 llvm 19를 설치하라고 안내하고 있습니다.
하지만 실제로 llvm 19만 설치한 다음 나머지 과정을 진행할 경우
clang-18이 존재하지 않는다는 에러를 보여주면서 설치를 실패하게 됩니다.

그리고, 받아온 Python파일을 압축 해제한 뒤 소스코드로부터 빌드를 진행합니다.

tar -xvzf Python-${PYTHON_VERSION}.tgz
cd Python-${PYTHON_VERSION}
./configure --enable-experimental-jit --with-zlib
sudo make
sudo make install

wsl2 ubuntu의 문제인지는 모르겠지만, --with-zlib가 없을경우, make install 과정에서 에러가 발생합니다.

이렇게 실행한 다음

아래 코드를 실행해 보면, 설치된 Python의 JIT을 사용하는지, 사용하지 않는지 확인할 수 있습니다.

import _testinternalcapi
print(f"JIT is enabled:{_testinternalcapi.get_optimizer()}")

image.png

이때 PYTHON_JIT이라는 환경변수의 값을 변경 함 으로써 JIT사용여부를 활성화 할 수 있습니다.
초기 빌드 과정에서 설정을 통해서 기본적으로 사용할 지 말지 여부를 결정할 수가 있습니다

image.png

2. JIT을 사용한 벤치마크

벤치마크에 앞서, JIT은 실행 전과, 실행 후 Just In Time Compile이 되면서 byte코드가 변하게 됩니다.

import dis
def list_add(a, b):
    temp = []
    for idx in range(len(a)):
        temp.append(a[idx]+b[idx])
    return temp
#해당 함수가 사용한 Byte Code를 출력해줌
for instruction in dis.Bytecode(list_add, adaptive=True):
    print(instruction.opname, instruction.argrepr)

lt1 = [i for i in range(500)]
lt2 = [i for i in range(500)]
list_add(lt1, lt2)
list_add(lt1, lt2)
print("="*100)
for instruction in dis.Bytecode(list_add, adaptive=True):
    print(instruction.opname, instruction.argrepr)

 

     
설명 첫번째 실행 전 두번 실행 후
코드 실행 재개 RESUME RESUME_CHECK
새로운 빈 리스트 생성 BUILD_LIST BUILD_LIST
temp 변수에 리스트 저장 STORE_FAST temp STORE_FAST temp
전역 변수 range 로드 LOAD_GLOBAL range LOAD_GLOBAL_BUILTIN range
전역 변수 len 로드 LOAD_GLOBAL len LOAD_GLOBAL_BUILTIN len
로컬 변수 a 로드 LOAD_FAST a LOAD_FAST a
함수 호출 CALL CALL_LEN
함수 호출 CALL CALL_BUILTIN_CLASS
반복 가능한 객체로 변환 (이터레이터 생성) GET_ITER GET_ITER
반복문 시작, L2로 점프 FOR_ITER to L2 FOR_ITER_RANGE to L2
idx 변수에 현재 반복값 저장 STORE_FAST idx STORE_FAST idx
temp 변수 로드 LOAD_FAST temp LOAD_FAST temp
tempappend 메소드 호출 LOAD_ATTR append LOAD_ATTR_METHOD_NO_DICT append
a[idx] 값 로드 LOAD_FAST a, idx LOAD_FAST a, idx
인덱싱 연산 (a[idx]) BINARY_SUBSCR BINARY_SUBSCR_LIST_INT
b[idx] 값 로드 LOAD_FAST b, idx LOAD_FAST b, idx
인덱싱 연산 (b[idx]) BINARY_SUBSCR BINARY_SUBSCR_LIST_INT
덧셈 연산 BINARY_OP + BINARY_OP_ADD_INT
함수 호출 CALL CALL_LIST_APPEND
스택에서 가장 위의 값 제거 POP_TOP POP_TOP
반복문 첫 번째 부분으로 점프 (L1) JUMP_BACKWARD to L1 JUMP_BACKWARD to L1
반복문 종료 END_FOR END_FOR
스택에서 가장 위의 값 제거 POP_TOP POP_TOP
temp 변수 로드 LOAD_FAST temp LOAD_FAST temp
temp 반환값 반환 RETURN_VALUE RETURN_VALUE

 

위와같이 ByteCode Level에서 최적화가 진행되고 있는 것을 볼 수 있습니다.

 

그러면, 실제 벤치마크를 진행 해보겠습니다.

import dis
def list_add(a, b):
    temp = []
    for idx in range(len(a)):
        temp.append(a[idx]+b[idx])
    return temp


if __name__ == "__main__":
    import time
    lt1 = lambda x : [i for i in range(x*500000)]
    lt2 = lambda x : [i for i in range(x*500000)]
    fin_ret = []
    for i in range(20):
        st = time.perf_counter()
        list_add(lt1(i), lt2(i))
        fin_ret.append(time.perf_counter()-st)
    print(fin_ret)

image.png


결과가 참 아이러니하게도, 오히려 jit을 사용했을 때 느려지는 것을 확인할 수 있습니다.

이는 reference로 사용한 문서에서도 역시 JIT을 사용했을 때 효과가 거의 미비한 것을 보여주고 있습니다.

아무래도 아직까지 실험 기능인 만큼, 충분히 개선 된 이후에 성능의 향상을 기대해 볼 수 있지 않을까 예상됩니다.

Reference

  1. https://docs.python.org/3/whatsnew/3.13.html#an-experimental-just-in-time-jit-compiler
  2. https://github.com/python/cpython/blob/main/Tools/jit/README.md
  3. https://realpython.com/python313-free-threading-jit/#jit-compiles-python-to-machine-code
728x90
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함