핵심 성과
| macOS AArch64 | +11~12% |
| x86_64 Linux | +5~6% |
벤치마크 범위: 20% 느려지는 경우부터 100% 이상 빨라지는 경우까지 다양 (unpack_sequence 마이크로벤치마크 제외)
- 목표 달성: 3.15 목표(5% 향상)를 1년 이상 조기 달성
- 프리 스레딩 지원: 아직 미완성, 3.15/3.16을 목표로 작업 중
핵심 교훈
-
스폰서 이탈 = 위기 → 커뮤니티 주도로 전환
Faster CPython 팀의 주요 스폰서가 2025년 철수했음에도, 커뮤니티 자발적 기여로 오히려 기여자가 늘어 성과를 냈습니다. -
복잡한 문제를 잘게 쪼개라
JIT처럼 진입장벽이 높은 프로젝트도, 단위 작업 분해 + 명확한 가이드 제공으로 C 프로그래머 경력의 비전문가가 기여할 수 있게 됐습니다. -
버스 팩터(bus factor) 감소가 건강한 프로젝트의 척도
중간 단계(optimizer) 기여자가 2명 → 4명으로 늘었고, 비코어 개발자도 핵심 기여자로 성장했습니다. -
측정 인프라가 개발 속도를 바꾼다
매일 JIT 성능을 리포트하는 시스템(doesjitgobrrr.com)이 회귀(regression) 조기 발견과 동기 부여 모두에 결정적이었습니다. -
커뮤니티 간 교류가 기술력을 높인다
PyPy 팀과의 교류, 컴파일러 개발자들과의 비공식 채팅이 실질적인 기술 발전으로 이어졌습니다.
배경 및 성과
[IMG] JIT 성능 그래프 (2026년 3월 17일 기준, 낮을수록 인터프리터보다 빠름)
(2026년 3월 17일 기준 JIT 성능. 낮을수록 인터프리터 대비 빠름. 이미지 출처: doesjitgobrrr.com)
좋은 소식입니다. macOS AArch64에서는 목표보다 1년 이상 일찍, x86_64 Linux에서는 몇 달 일찍 CPython JIT의 (매우 겸손한) 성능 목표를 달성했습니다. 3.15 알파 JIT는 macOS AArch64에서 테일 콜링 인터프리터보다 약 11~12% 빠르고, x86_64 Linux에서 표준 인터프리터보다 5~6% 빠릅니다. 이 수치는 기하 평균이며 예비 수치입니다. 실제 범위는 20% 느려지는 경우부터 100% 이상 빨라지는 경우까지 다양합니다(unpack_sequence 마이크로벤치마크 제외). 아직 제대로 된 프리 스레딩 지원은 없지만, 3.15/3.16에서 이를 목표로 하고 있습니다. JIT는 이제 다시 궤도에 올랐습니다.
이것이 얼마나 힘들었는지 아무리 강조해도 지나치지 않습니다. JIT 프로젝트가 과연 의미 있는 속도 향상을 이뤄낼 수 있을지 진지하게 의문이 들었던 시점이 있었습니다. 돌이켜보면, 원래 CPython JIT는 사실상 속도 향상이 없었습니다. 8개월 전, 저는 3.13과 3.14의 원래 CPython JIT가 인터프리터보다 오히려 느린 경우가 많다는 JIT 회고 글을 올렸습니다. 그때는 Faster CPython 팀이 주요 스폰서의 지원을 잃은 시점이기도 했습니다. 저는 자원봉사자라 직접적인 영향은 없었지만, 그곳에서 일하는 동료들에게는 영향이 있었고, 한때 JIT의 미래가 불확실해 보였습니다.
그렇다면 3.13, 3.14와 무엇이 달라졌을까요? 저는 우리의 기지로 JIT를 실패의 위기에서 구해냈다는 영웅담을 늘어놓지 않겠습니다. 현재의 성공 대부분은 운 덕분이라고 솔직히 생각합니다. 적절한 시기, 적절한 장소, 적절한 사람, 적절한 선택. 핵심 JIT 기여자인 Savannah Ostrowski, Mark Shannon, Diego Russo, Brandt Bucher, 그리고 저 중 단 한 명이라도 없었다면 이것은 가능하지 않았을 것이라고 진지하게 생각합니다. 다른 활발한 JIT 기여자들도 빠뜨리지 않기 위해 몇 분을 더 언급하겠습니다: Hai Zhu, Zheaoli, Tomas Roun, Reiden Ong, Donghee Na, 그리고 빠뜨린 분들도 계실 것입니다.
저는 JIT에서 잘 언급되지 않는 부분인 사람과 약간의 운에 대해 이야기하려 합니다. 기술적인 세부 사항이 궁금하시다면 여기를 참고하세요.
파트 1: 커뮤니티 주도 JIT
Faster CPython 팀은 2025년에 주요 스폰서를 잃었습니다. 저는 즉시 커뮤니티 주도 관리 아이디어를 제안했습니다. 당시 이것이 잘 될지 꽤 불확실했습니다. JIT 프로젝트는 신규 기여자에게 좋지 않은 것으로 알려져 있습니다. 역사적으로 많은 사전 전문 지식이 필요합니다.
케임브리지에서 열린 CPython 코어 스프린트에서 JIT 코어 팀은 만나, 3.15까지 5% 빠른 JIT, 3.16까지 10% 빠른 JIT와 프리 스레딩 지원을 위한 계획을 작성했습니다. 덜 주목받았지만 프로젝트 건강에 필수적인 사항이 있었는데, 바로 버스 팩터 감소였습니다. JIT의 세 단계(프론트엔드의 리전 셀렉터, 미들엔드의 옵티마이저, 백엔드의 코드 생성기) 모두에서 2명 이상의 활발한 유지 관리자를 원했습니다.
이전에는 JIT 미들엔드에 단 2명의 활발한 반복 기여자만 있었습니다. 오늘날 JIT 미들엔드에는 4명의 활발한 반복 기여자가 있으며, 비코어 개발자 2명(Hai Zhu와 Reiden)은 유능하고 소중한 멤버로 성장했습니다.
사람들을 끌어들이는 데 효과적이었던 것은 일반적인 소프트웨어 엔지니어링 관행이었습니다: 복잡한 문제를 관리 가능한 부분으로 분해하기. Brandt는 3.14에서 이를 먼저 시작했는데, JIT 최적화를 단순한 작업으로 나누는 여러 메가 이슈를 열었습니다. 예를 들어 "JIT에서 단일 명령어를 최적화해보세요"라고 하는 식이었습니다. 저는 Brandt의 아이디어를 이어받아 3.15에서도 적용했습니다. 다행히 저는 더 쉬운 작업을 맡았는데, 인터프리터 명령어를 쉽게 최적화 가능한 형태로 변환하는 이슈였습니다. 새로운 기여자를 장려하기 위해 매우 자세한 지침을 즉시 실행 가능하도록 정리했습니다. 또한 작업 단위를 명확하게 구분했습니다. 이것이 도움이 된 것 같습니다. 저를 포함해 11명의 기여자가 그 이슈에서 작업하며 거의 전체 인터프리터를 JIT 옵티마이저 친화적인 형태로 변환했습니다. 핵심은 JIT를 불투명한 덩어리에서 JIT 경험이 없는 C 프로그래머도 기여할 수 있는 것으로 분해했다는 점입니다.
다른 효과적인 방법들: 사람들을 격려하고 크고 작은 성취를 축하하기. 모든 JIT PR에는 명확한 결과물이 있었고, 이것이 사람들에게 방향감을 주었을 것이라 생각합니다.
커뮤니티 최적화 노력은 성과를 냈습니다. JIT는 x86_64 Linux에서 1% 빠른 것에서 3~4% 빠른 것으로 발전했습니다(아래 파란 선 참고):
[IMG] 커뮤니티 최적화 기간 동안의 JIT 성능 vs 인터프리터
(이미지 출처: doesjitgobrrr.com)
파트 2: 운 좋은 선택들
트레이스 레코딩 (Trace Recording)
다시 말하지만 이것의 대부분은 운 덕분이라고 생각합니다. 케임브리지 CPython 코어 스프린트에서 Brandt가 저를 설득해 JIT 프론트엔드를 트레이싱 방식으로 재작성하게 했습니다. 처음에는 좋아하지 않았지만, 일종의 의기로 "작동하지 않는다는 걸 증명하기 위해" 재작성해보자고 생각했습니다.
초기 프로토타입은 3일 만에 작동했지만, 테스트 스위트를 통과하면서 제대로 JIT하게 만드는 데 한 달이 걸렸습니다. 초기 결과는 형편없었습니다. x86_64 Linux에서 약 6% 느렸습니다. 포기하려던 순간, 운 좋은 사고가 일어났습니다: Mark의 제안을 잘못 이해했던 것입니다.
Mark는 디스패치 테이블을 인터프리터에 스레드로 연결하여 인터프리터에 두 개의 디스패치 테이블(일반 인터프리터용 하나, 트레이싱용 하나)을 갖자고 제안했습니다. 그런데 저는 잘못 이해하고 더 극단적인 버전을 만들었습니다: 일반 명령어의 트레이싱 버전 대신, 트레이싱을 담당하는 하나의 명령어만 두고 두 번째 테이블의 모든 명령어가 그것을 가리키도록 했습니다. 이것이 정말 좋은 선택으로 드러났습니다. 초기 이중 테이블 방식은 인터프리터 크기가 두 배로 늘어나 코드 비대와 자연스러운 속도 저하를 유발해 훨씬 느렸습니다. 단 하나의 명령어와 두 테이블을 사용함으로써 인터프리터 크기를 명령어 1개만큼만 증가시키고 기본 인터프리터를 매우 빠르게 유지할 수 있었습니다. 이 메커니즘을 애정을 담아 듀얼 디스패치(dual dispatch) 라고 부릅니다.
트레이스 레코딩이 얼마나 중요했는지 보여주는 수치: JIT 코드 커버리지가 50% 증가했습니다. 이는 미래의 모든 최적화가 (단순화해서) 50% 덜 효과적이었을 것임을 의미합니다.
Brandt와 Mark가 이런 멋진 해결책을 우연히 발견하도록 이끌어 주었음에 감사합니다.
참조 카운트 제거 (Reference Count Elimination)
또 다른 운 좋은 선택은 참조 카운트 제거를 시도한 것이었습니다. 이것은 원래 Matt Page가 CPython 바이트코드 옵티마이저에서 한 작업입니다. 저는 바이트코드 옵티마이저 작업에도 불구하고 참조 카운트 감소마다 JIT된 코드에 분기(branch)가 하나 남아 있다는 것을 발견했습니다. "이 분기를 제거해보면 어떨까"라고 생각했고, 얼마나 도움이 될지 전혀 몰랐습니다. 단 하나의 분기도 실제로 꽤 비쌌고, 모든 Python 명령어마다 분기가 1개 이상 있다면 이것들이 쌓입니다.
또 다른 운 좋은 점은 이것이 병렬화하기 얼마나 쉽고, 인터프리터와 JIT를 사람들에게 가르치는 좋은 도구가 되었다는 것입니다. 이것이 Python 3.15 JIT에서 사람들에게 작업을 지시한 주요 최적화였습니다. 대부분 수동 리팩터링 과정이었지만, 사람들에게 JIT의 핵심 부분을 압도하지 않으면서 학습할 기회가 되었습니다.
파트 3: 훌륭한 팀
우리에게는 훌륭한 인프라 팀이 있습니다. 사실 이것은 한 사람입니다. 실제로 우리의 "팀"은 현재 Savannah의 옷장에서 돌아가는 4대의 머신입니다. 그럼에도 불구하고 Savannah는 JIT를 위해 인프라 팀 전체에 해당하는 작업을 해냈습니다. 성능 수치를 보고할 방법이 없었다면 JIT는 이렇게 빠르게 진전될 수 없었을 것입니다. 매일 JIT 실행 결과는 피드백 루프에서 게임 체인저였습니다. JIT 성능의 회귀를 잡는 데 도움이 되었고, 우리의 최적화가 실제로 작동한다는 것을 확인할 수 있게 했습니다.
Mark는 기술적으로 탁월합니다. 인터넷이 이미 그에게 너무 많은 찬사를 준다고 생각하기에 더 이상 말하지 않겠습니다 :).
Diego도 훌륭합니다. ARM 하드웨어의 JIT를 담당하고 있으며, 최근에는 JIT를 프로파일러 친화적으로 만드는 작업을 시작했습니다. 이것이 얼마나 어려운 문제인지 아무리 강조해도 지나치지 않습니다.
Brandt는 머신 코드 백엔드의 원래 기반을 다졌습니다. 이것이 없었다면 새로운 기여자들이 어셈블러를 작성해야 했을 것이고, 아마도 더 많은 사람들을 멀어지게 했을 것입니다.
파트 4: 사람들과 이야기하기
사람들과 이야기하고 아이디어를 공유하는 것의 가치를 강조하고 싶습니다.
PyPy에 대해 많이 가르쳐 준 CF Bolz-Tereick에게 감사합니다. 몇 달 동안 PyPy 소스 코드를 살펴보았고, 이것이 저를 전반적으로 더 나은 JIT 개발자로 만들었다고 생각합니다. CF는 도움이 필요할 때 매우 친절하게 도와주었습니다.
저는 Max Bernstein과 친목 컴파일러 채팅을 함께 하고 있는데, 이것이 없었다면 오래전에 동기를 잃었을 것입니다. Max는 다작 작가이자 친근한 컴파일러 전문가입니다.
아이디어는 고립된 공간에서 존재하지 않습니다. 한동안 컴파일러 개발자들과 어울린 덕분에 JIT 작성이 더 능숙해졌다고 생각합니다. 최소한 PyPy를 살펴본 것이 제 시야를 넓혀 주었습니다!
결론
사람이 중요합니다. 그리고 약간의 운이 더해지면, JIT go brrr.

8 hours ago
1







English (US) ·