CPython Core Dev Sprint에서 실제 세계의 JIT Trace
(antocuni.eu)CPython의 새로운 추적 JIT(Just-In-Time) 컴파일러가 직면한 기술적 과제에 대한 심층적인 요약입니다.
추적 차단기 (Trace Blockers)
추적 JIT는 프로그램 실행 중 자주 실행되는 코드 경로("핫 경로") 를 식별하고, 해당 경로의 마이크로 연산(micro-operations)을 기록하여 최적화된 기계 코드를 생성합니다. 그러나 JIT가 내부를 들여다볼 수 없는 코드, 즉 "추적 차단기" 를 만나면 이 과정이 중단됩니다. CPython의 경우, C로 작성된 확장 함수를 호출하는 것이 대표적인 예입니다.
-
기술적 설명: 블로그에서는 원주율(π)을 계산하는 순수 파이썬 함수를 예로 듭니다. PyPy의 JIT는 이 숫자 계산 루프를 매우 효율적으로 최적화하여 CPython보다 42배나 빠른 성능을 보입니다. 하지만 루프 내부에 JIT가 추적할 수 없는 C 함수 호출(
hic_sunt_leones()
)을 단 하나 추가하면, PyPy의 성능은 CPython보다 겨우 1.8배 빠른 수준으로 급격히 떨어집니다. 이 단일 "추적 차단기"가 JIT의 최적화 능력을 대부분 무력화시킨 것입니다. 이는 JIT가 C 함수의 내부 동작을 알 수 없기 때문에 루프 전체를 하나의 단위로 최적화하지 못하고, C 함수 호출 전후로 코드를 분리하여 처리하기 때문입니다.
데이터 기반 제어 흐름 (Data-Driven Control Flow)
이 문제는 입력 데이터에 따라 프로그램의 제어 흐름이 크게 달라질 때 발생합니다. 추적 JIT는 일관된 "핫 경로"가 존재한다는 가정 하에 가장 효과적으로 작동하지만, 데이터에 따라 실행 경로가 계속 바뀌면 이러한 가정이 깨지게 됩니다.
-
기술적 설명: 블로그에서는 9개의 인자를 받는 함수를 예로 듭니다. 각 인자는
None
이거나 숫자일 수 있으며, 함수 내부에는if <var> is None: ...
형태의 조건문이 연이어 나타납니다. 이 경우, 인자로 들어오는None
값의 조합에 따라 실행되는 코드 경로가 매번 달라집니다. 따라서 JIT는 "지수적 추적(exponential number of traces)" 문제에 직면하게 됩니다. 즉, JIT는None
인자의 모든 가능한 조합에 대해 별도의 최적화된 코드를 생성하려고 시도하게 되며, 이는 엄청난 오버헤드를 유발하여 결국 JIT를 사용하지 않는 CPython보다도 훨씬 느려지는 결과를 초래합니다.
결론적으로, 이 블로그 게시물은 CPython의 새로운 추적 JIT가 성공적으로 안착하기 위해서는 이러한 "추적 차단기" 와 "데이터 기반 제어 흐름" 문제를 해결해야 한다고 강조합니다. 이는 단순한 구현의 문제가 아니라 추적 JIT 기술 자체가 가진 근본적인 한계일 수 있음을 시사합니다.