CPython 실험적 JIT, main에서 제거된다 — PEP 836 철회와 운영위원회가 요구한 10가지

CPython의 실험적 JIT(Just-In-Time) 컴파일러가 main 브랜치에서 빠집니다. JIT는 2024년 Python 3.13에 실험적 기능으로 들어왔습니다. 올해 6월 Python 운영위원회(steering council)는 “6개월 안에 표준 트랙 PEP를 승인받지 못하면 JIT 코드를 제거한다”고 통보했습니다. JIT 팀은 PEP 836을 제출했지만 10월 19일 스스로 철회했습니다. 남은 기여자들로는 운영위원회의 요구를 감당할 여력이 없다는 이유입니다. 이 글에서는 JIT가 어떻게 들어왔고 왜 빠지게 됐는지, 그리고 지금 쓰는 Python에서 JIT 상태를 확인하는 방법을 정리합니다.

PEP 없이 들어온 JIT

CPython JIT는 브랜트 부커(Brandt Bucher)가 2024년 초에 머지했습니다. 이 정도 규모의 기능은 보통 운영위원회가 표준 트랙(standards-track) PEP를 승인한 뒤에 들어갑니다. 부커는 코어 개발자 스프린트에서 발표하고 머지 의사를 밝혔지만 PEP는 쓰지 않았습니다. 당시 3.13 릴리스 매니저였던 토머스 바우터스(Thomas Wouters)의 요청을 받고 나서야 PEP 744를 작성했습니다.

PEP 744는 정보성(informational) PEP였습니다. 커뮤니티에 정보를 알릴 뿐, 기능을 진행한다는 공식 합의는 아닙니다. JIT는 이후 2년 넘게 이 PEP 하나를 근거로 main에서 개발됐습니다.

운영위원회의 요구와 PEP 836

운영위원회가 올해 문제를 제기한 데에 특정 계기는 없었습니다. 바우터스는 JIT가 다른 작업과 부딪히지 않게 하려는 것이라고 설명했습니다. 계측(instrumentation)·디버깅·프로파일링 도구와의 호환, CinderX 같은 외부 JIT, Numba와 PyTorch compile 같은 인접 도구가 그 예입니다. 운영위원회는 3.15 베타가 나올 때까지 기다렸다가 요청했습니다. 3.15의 현 상태에는 문제가 없다는 점을 분명히 하기 위해서입니다.

날짜사건
2024년 초부커가 JIT 머지. 이후 사바나 오스트로스키(Savannah Ostrowski)와 함께 PEP 744 작성
2026년 6월운영위원회가 표준 트랙 PEP 요구. 기한은 6개월
7월 3일부커, 오스트로스키, 켄 진(Ken Jin)이 PEP 836 “JIT Go Brrr” 공개
8월 6일운영위원회가 작성자들과 면담
9월 21일운영위원회가 10개 항목의 보완 요구 발표
10월 19일오스트로스키가 PEP 철회 발표

PEP 836은 JIT를 “지원되는 정식 기능”으로 만드는 2년 반짜리 로드맵이었습니다. 핵심 목표는 다음과 같습니다.

시점목표
1년차 (3.16 첫 베타)프런트엔드를 트레이스(trace) 기반에서 메서드(method) 기반으로 전환. free-threaded 빌드와 호환
2년차 (3.17 첫 베타)JIT + free-threaded 조합이 free-threaded 인터프리터보다 pyperformance 기하평균 20% 이상 빠를 것. 이 수치를 main에 남기 위한 최소 조건으로 제시
2.5년차 (3.17 첫 RC, 2028년 8월)인기 PyPI 패키지의 테스트 스위트를 JIT로 실행하는 호환성 리뷰

PEP 부록에 실린 3.15 JIT의 성능은 다음과 같습니다. 2026년 6월 16~27일 측정값입니다.

머신구성평균 속도 향상
M3 Pro, macOSJIT + tail call12.6%
AmpereOne, Linux aarch64JIT7.3%
i5-8400, Linux x86_64JIT6.9%
Ryzen 5 3600X, WindowsJIT + tail call4.7%

운영위원회가 지적한 10개 항목

9월 21일 성명에서 운영위원회는 PEP 836이 요청한 질문에 “대부분 답했다”고 평가했습니다. 다만 일부 답은 설득력이 약하다고 했습니다. 운영위원회가 판단하려던 것은 JIT가 정식 기능이 될 기준이 아니었습니다. JIT가 main에서 실험을 계속해도 되는지였습니다. main에서 실험을 하면 설계가 하나로 모이지 않습니다. 실험 중에 동작과 API가 바뀌면 코어 개발자, 패키지 메인테이너, 사용자 모두가 불확실성을 떠안습니다.

영역운영위원회의 지적
플랫폼 동등성새 플랫폼에 쉽게 이식할 수 있어야 함. 특정 플랫폼 지식이 있어야만 유지보수할 수 있는 설계는 안 됨
사용자 영향PEP가 “지나치게 낙관적”. 사용자가 인터프리터의 특이 동작에 기대는 경우가 많으니 동작 차이를 버그로 볼지, 피할 수 없으면 어떻게 할지 명시할 것
보안“PEP에서 소홀히 다룬 영역”. Python 보안 대응팀과 논의하고, 정식 지원 조건에 보안 리뷰를 넣을 것
호환성 리뷰 시점2028년 8월은 “너무, 너무 늦다”. 가능한 한 빨리 시작해 계속 진행할 것
설계 결정 과정CPython 유지보수를 책임지는 더 넓은 개발자 집단의 비판적 리뷰 없이 결정되고 있음
그 밖의 항목지원 계획과 성능 기준이 구체적이지 않음. 메모리 영향 등

켄 진은 며칠 뒤 피드백에 감사하며 반영하겠다고 답했습니다. 하지만 한 달이 안 돼 결론이 바뀌었습니다.

철회 그리고 그 이후

오스트로스키는 3.16·3.17 릴리스 매니저이자 현 운영위원입니다. 그는 10월 19일 철회를 발표하며 이렇게 썼습니다.

운영위원회가 이 정도 범위의 제안에 높은 기준을 두는 것은 옳습니다. 하지만 지금 버전을 끌고 갈 개인적 여력이 없습니다. PEP에 담은 기술적 방향, 즉 free-threaded 지원과 메서드 기반 프런트엔드는 여전히 옳다고 믿습니다. 참조 구현은 철회된 PEP에 링크해 누구든 이어받을 수 있게 하겠습니다.

브렛 캐넌(Brett Cannon)은 main 저장소에서 작업이 멈추는 것이고, 다른 곳에서 이어갈지는 커뮤니티가 정할 일이라고 했습니다. 기한 안에 대안 PEP가 나올 가능성은 낮게 봤습니다. JIT를 이끌던 사람이 모두 PEP 836의 작성자이거나 기여자이기 때문입니다. JIT 코드는 제거로 생기는 문제를 일찍 잡을 수 있도록 3.16 개발 주기 초반에 빠질 전망입니다.

LWN 댓글에서는 왜 CPython JIT가 유독 어려운지가 화제였습니다. 여러 사람이 공통으로 짚은 원인은 C API입니다. CPython C API는 불투명 핸들(opaque handle) 대신 인터프리터 자료구조를 가리키는 포인터를 그대로 넘깁니다. 그래서 JIT가 고를 수 있는 길이 셋뿐입니다. API 호환을 버려 채택률을 잃거나, tagged pointer 같은 핵심 최적화를 포기하거나, API를 그때그때 흉내 내 성능이 확장 모듈에 따라 들쭉날쭉해지는 것입니다.

지금 쓰는 Python의 JIT 상태 확인

JIT는 --enable-experimental-jit로 빌드해야 들어갑니다. python-build-standalone(uv가 설치하는 Python)은 yes-off로 빌드돼 있습니다. JIT 코드는 들어 있지만 기본으로 꺼져 있고, PYTHON_JIT=1 환경 변수로 켭니다. 3.14부터는 sys._jit로 상태를 볼 수 있습니다.

import sys, sysconfig
print(sys.version.split()[0])
print("configure:", "--enable-experimental-jit" in (sysconfig.get_config_var("CONFIG_ARGS") or ""))
print("available:", sys._jit.is_available())
print("enabled:  ", sys._jit.is_enabled())
uv python install 3.15
python3.15 jitcheck.py
PYTHON_JIT=1 python3.15 jitcheck.py | tail -1
3.15.2
configure: True
available: True
enabled:   False
enabled:   True

단순 정수 루프로 체감 차이를 보면 다음과 같습니다. 같은 3.15.2에서 JIT를 끄고 켜서 세 번씩 실행했습니다.

import time
def f(n):
    s = 0
    for i in range(n):
        s += i * i % 7
    return s
t = time.perf_counter(); f(20_000_000); print(f"{time.perf_counter()-t:.2f}s")
for i in 1 2 3; do
  echo -n "off "; python3.15 jitbench.py
  echo -n "on  "; PYTHON_JIT=1 python3.15 jitbench.py
done
off 0.92s
on  0.92s
off 0.93s
on  0.89s
off 0.90s
on  0.91s

이 루프에서는 측정 오차 수준의 차이만 납니다. PEP가 제시한 4~12%도 pyperformance 전체의 기하평균입니다. 일반 사용자가 JIT 제거를 체감할 일은 거의 없다는 뜻입니다.

정리

이번 결정은 기술적 실패라기보다 절차와 인력의 문제입니다. JIT는 표준 트랙 PEP 없이 main에 들어왔습니다. 운영위원회가 절차를 바로잡으려 했을 때는 작업을 감당할 사람이 몇 명뿐이었습니다. LWN 댓글에서도 모두가 예의를 지키며 절차 문제를 정리했다는 평가가 많았습니다.

빌드 스크립트나 CI에서 --enable-experimental-jit나 PYTHON_JIT를 쓰고 있다면 3.16 알파부터 정리해야 합니다. 성능이 필요하면 free-threaded 빌드, PyPy, 네이티브 확장 모듈을 비교하십시오. 메서드 기반 프런트엔드 프로토타입은 GIL 빌드에서 5~6% 빨랐다고 합니다. 누가 그 코드를 이어받을지가 CPython JIT의 다음 장을 결정할 것입니다.

참고

답글 남기기