[Deferred] PEP 774 - Removing the LLVM requirement for JIT builds
원문 링크: PEP 774 - Removing the LLVM requirement for JIT builds
상태: Deferred 유형: Standards Track 작성일: 27-Jan-2025
PEP 774: JIT 빌드 시 LLVM 요구사항 제거 제안
초록 (Abstract)
Python 3.13부터 CPython은 --enable-experimental-jit
(Linux 및 Mac) 또는 --experimental-jit
(Windows) 플래그를 통해 실험적인 JIT (Just-In-Time) 컴파일러를 사용하여 구성 및 빌드될 수 있었습니다. JIT가 활성화된 CPython을 빌드하려면 사용자 머신에 LLVM이 설치되어 있어야 합니다 (초기에는 LLVM 16, 최근에는 LLVM 19). LLVM은 copy-and-patch JIT에 필수적인 스텐실(stencil)을 생성하는 역할을 합니다 (PEP 744 참조). 이러한 스텐실은 런타임에 머신 코드를 생성하는 데 사용되는 미리 정의된 아키텍처별 템플릿입니다.
이 PEP는 생성된 스텐실을 CPython 저장소에 호스팅하여 JIT 지원 빌드에 대한 LLVM 빌드 시간 종속성을 제거할 것을 제안합니다. 이 접근 방식은 지원되는 플랫폼에 대해 미리 체크인된 스텐실을 빌드 시 활용할 수 있게 하여, 기여자 경험을 단순화하고 2024년 9월 Python 핵심 개발자 스프린트에서 제기된 우려 사항을 해결합니다. 그러나 개발자 경험 개선은 저장소 크기 증가라는 명확한 트레이드오프가 있습니다.
이 PEP는 JIT 자체를 수용하거나 거부하는 제안이 아니라, JIT 빌드에 대한 LLVM 빌드 시간 종속성이 향후에도 허용 가능한지 여부를 결정하는 것임을 주목하는 것이 중요합니다. 만약 이 PEP가 거부되면, LLVM 빌드 시간 요구사항을 유지하는 현재 상태(status quo)로 진행될 것입니다. 이 종속성은 지금까지 JIT 개발 프로세스에 효과적으로 기여했지만, 이 PEP가 완화하고자 하는 설정 복잡성과 추가적인 문제를 야기합니다.
동기 (Motivation)
2024년 9월에 열린 Python 핵심 개발자 스프린트에서 JIT의 다음 단계에 대한 논의가 있었고, 이와 관련된 논의는 GitHub에서도 진행되었습니다. 이 논의의 일환으로, 3.14 버전에서 JIT를 기본값으로 제공하기 위한 준비로 JIT 빌드에 대한 LLVM 요구사항을 제거하려는 분명한 의지가 있었습니다. 스프린트의 합의는 Tier 1 플랫폼의 디버그가 아닌 빌드에 대해 미리 생성된 스텐실을 제공하는 것으로 충분하며, 이러한 파일을 CPython 저장소에 체크인하는 것이 제한된 수의 플랫폼에 적합하다는 것이었습니다 (더 많은 옵션이 탐색되었지만, ‘Rejected Ideas’ 참조).
현재 JIT가 포함된 CPython을 빌드하려면 LLVM이 빌드 시간 종속성으로 필요합니다. 이 종속성은 최종 사용자에게 노출되지 않지만 최적의 상태는 아닙니다. LLVM을 요구하는 것은 개발자와 JIT가 활성화된 CPython을 빌드하려는 사람들에게 설정 부담을 추가합니다. 운영 체제에 따라 OS와 함께 제공되는 LLVM 버전이 JIT 빌드에 필요한 버전과 다를 수 있어 문제 해결 및 해결에 추가적인 복잡성을 초래합니다. 현재 JIT 관련 코드에 기여하고 유지 관리하는 핵심 개발자가 소수이므로, JIT 관련 코드 작업을 위한 마찰을 가능한 한 최소화하고자 합니다.
제안된 접근 방식에 따르면, 지원되는 아키텍처를 위한 사전 컴파일된 스텐실은 미리 생성되어 중앙 위치에 저장되고 빌드 중에 자동으로 사용될 수 있습니다. 이 접근 방식은 재현 가능한 빌드를 보장하여 JIT가 CPython의 미래에 더 안정적이고 지속 가능한 부분이 되도록 합니다.
근거 (Rationale)
이 PEP는 LLVM에 대한 빌드 시간 종속성을 제거하기 위한 최선의 방법으로 JIT 스텐실을 CPython 저장소에 직접 체크인할 것을 제안합니다.
이 접근 방식은 다음을 제공합니다:
- JIT와 함께 CPython을 빌드하려는 사람들에게 최고의 엔드-투-엔드 경험을 제공합니다.
- JIT에 기여하려는 사람들의 진입 장벽을 낮춥니다.
- 외부 인프라나 다운로드 메커니즘에 의존하지 않고 플랫폼 전반에 걸쳐 빌드가 재현 가능하고 일관성을 유지하도록 보장합니다.
- 네트워크 조건이나 호스팅된 파일과 CPython 저장소 상태 간의 잠재적 불일치로 인해 발생하는 가변성을 제거합니다.
- 모든 다른 JIT 관련 코드와 동일한 검토 프로세스에 스텐실을 적용합니다.
그러나 이 접근 방식은 전체 저장소 크기를 약간 증가시킵니다. 지난 90일 동안 커밋의 저장소 증가를 비교하면, 실제 커밋과 스텐실이 추가된 동일한 커밋 간의 차이는 스텐실 파일당 0.03MB에 해당합니다. 이는 같은 기간 동안 2.55MB 증가한 전체 저장소 크기 내에서 작은 증가입니다. 6개의 스텐실 파일의 경우, 이는 최대 0.18MB에 해당합니다. 6개 플랫폼에 대한 스텐실 파일의 현재 총 크기는 7.2MB입니다.
레지스터 할당 변경과 함께 스텐실은 미래에 더 커질 수 있으며, 각 스텐실 파일에 명령어당 5-6가지 변형(5-6배 더 커짐)을 도입할 수 있습니다. 그러나 이 경로를 따르게 된다면, 이러한 크기 증가를 상쇄하는 데 도움이 될 수 있는 스텐실 파일에 대한 추가 수정(예: 주석 제거, 스텐실 최소화)이 있을 수 있습니다.
명세 (Specification)
이 명세는 LLVM에 대한 빌드 시간 종속성을 제거하기 위한 제안된 변경 사항과 이 PEP가 수락될 경우 기여자 경험을 간략하게 설명합니다.
저장소 변경 사항 (Repository changes)
CPython 저장소는 이제 Tools/jit
아래의 stencils/
라는 새 하위 디렉토리에 사전 컴파일된 JIT 스텐실을 호스팅할 것입니다. 현재 JIT는 6개의 플랫폼에 대해 테스트 및 빌드되므로, 처음에는 6개의 스텐실 파일을 체크인할 것입니다. 미래에는 추가 플랫폼에 대한 지원이 필요하거나 관련성이 있을 경우 추가 스텐실 파일을 체크인할 수 있습니다.
cpython/
Tools/
jit/
stencils/
aarch64-apple-darwin.h
aarch64-unknown-linux-gnu.h
i686-pc-windows-msvc.h
x86_64-apple-darwin.h
x86_64-pc-windows-msvc.h
x86_64-pc-linux-gnu.h
워크플로우 (Workflow)
워크플로우 변경 사항은 JIT가 활성화된 CPython 빌드와 JIT 구현 작업이라는 두 부분으로 나눌 수 있습니다.
JIT가 활성화된 CPython 빌드 (Building CPython with the JIT)
사전 컴파일된 JIT 스텐실 파일은 Tools/jit/stencils
디렉토리에 저장되며, 각 파일 이름은 위에서 설명한 대상 트리플(target triple)에 해당합니다. 빌드 시, 체크인된 스텐실을 사용할지 또는 사용자 플랫폼에 대한 새 스텐실을 생성할지 결정합니다. 특히 LLVM이 설치된 기여자의 경우, Tools/jit/stencils
의 build.py
스크립트는 해당 플랫폼의 스텐실을 재생성할 수 있도록 합니다. LLVM이 없는 사용자는 저장소에서 직접 사전 컴파일된 스텐실 파일을 사용할 수 있습니다.
JIT 구현 작업 (Working on the JIT’s implementation (or touching JIT files))
CI (Continuous Integration)에서 JIT 관련 파일이 변경될 때 스텐실 파일은 자동으로 유효성이 검사되고 업데이트됩니다. 이 파일에 영향을 미치는 Pull Request가 열리면, 빌드를 빌드하고 테스트하는 jit.yml
워크플로우가 평소와 같이 실행됩니다.
그러나 이 과정의 일환으로, 저장소의 현재 스텐실과 CI에서 생성된 스텐실을 비교하는 새로운 단계를 도입할 것입니다. 플랫폼의 스텐실 파일에 차이(diff)가 있는 경우, 업데이트된 스텐실에 대한 패치 파일이 생성되고 해당 단계는 실패합니다. 각 패치는 GitHub Actions에 업로드됩니다. CI가 모든 플랫폼에서 실행을 완료한 후, 편의를 위해 패치들이 단일 패치 파일로 집계됩니다. 이 집계된 패치를 다운로드하여 로컬에 적용하고 업데이트된 스텐실을 다시 브랜치에 커밋할 수 있습니다. 그러면 다음 CI 실행은 통과할 것입니다.
참조 구현 (Reference Implementation)
참조 구현의 주요 부분은 다음과 같습니다:
.github/workflows/jit.yml
: 스텐실 패치를 생성하는 CI 워크플로우.Tools/jit/stencils
: 스텐실이 저장되는 디렉토리.Tools/jit/_targets
: 빌드 시 템플릿을 컴파일하고 파싱하는 코드.
스텐실 자체와 필요한 JIT README 변경 사항을 제외하고, 재현 가능한 스텐실 생성 및 호스팅을 지원하기 위한 소스 코드 변경 사항은 최소한입니다 (약 150줄 변경).
거부된 아이디어 (Rejected Ideas)
이 PEP에 대한 연구 및 탐색의 일환으로 여러 대안적 접근 방식이 고려되었습니다. 그러나 아래 아이디어들은 인프라 비용, 유지 관리 부담 또는 더 나쁜 전반적인 개발자 경험을 수반합니다.
- Git 서브모듈 사용 (Using Git submodules): Git 서브모듈은 스텐실 호스팅에 대한 좋지 않은 개발자 경험을 제공합니다. JIT에 대한 모든 업데이트는 스텐실을 재생성하고 별도의 저장소에 커밋하는 것을 필요로 합니다. 이는 서브모듈 저장소에서 스텐실을 업데이트하고, 해당 변경 사항을 커밋한 다음, 메인 CPython 저장소에서 서브모듈 참조를 업데이트해야 하는 복잡한 프로세스를 야기합니다. 이러한 단절은 불필요한 복잡성과 오버헤드를 추가하여 기여자와 유지 관리자에게 프로세스를 취약하고 오류 발생 가능성이 높게 만듭니다.
- Git 서브트리 사용 (Using Git subtrees): 서브트리를 사용할 때, 임베디드 저장소는 이 PEP에서 제안된 것과 유사하게 메인 저장소의 일부가 됩니다. 그러나 서브트리는 유지 관리를 위해 추가적인 툴링 및 단계를 필요로 하므로 워크플로우에 불필요한 복잡성을 더합니다.
- 별도 저장소에서 호스팅 (Hosting in a separate repository): JIT 스텐실을 별도의 저장소로 분리하면 스텐실 호스팅과 관련된 스토리지 오버헤드를 피할 수 있지만, 빌드 프로세스에 복잡성을 더합니다. 스텐실을 가져오기 위한 추가 툴링이 필요하며, 워크플로우에 추가적이고 불필요한 실패 지점을 생성할 수 있습니다. 또한, 이 분리는 저장소 간에 업데이트를 조정해야 하므로 스텐실과 CPython 소스 트리 간의 일관성을 보장하기 어렵게 만듭니다.
- 클라우드 스토리지에서 호스팅 (Hosting in cloud storage): S3 버킷이나 GitHub 원시 스토리지와 같은 클라우드 스토리지에서 스텐실을 호스팅하는 것은 외부 종속성을 도입하여 오프라인 개발 워크플로우를 복잡하게 만듭니다. 또한, 공급업체에 따라 이러한 유형의 호스팅은 추가 비용이 발생할 수 있으며, 이는 피하고자 하는 바입니다.
- Git LFS 사용 (Using Git LFS): Git Large File Storage (LFS)는 기여자에게 툴 종속성을 추가하여 개발 워크플로우를 복잡하게 만듭니다. 특히 Git LFS를 이미 사용하지 않는 사람들에게 그렇습니다. Git LFS는 LFS에 의해 관리되는 파일이 특정 커밋을 체크아웃할 때 가져오기 위해 인터넷 연결이 필요하므로 오프라인 워크플로우와 잘 작동하지 않으며, 이는 기본적인 Git 워크플로우에도 지장을 줍니다. Git LFS에는 무료 할당량이 있지만, 할당량을 초과하면 추가 비용이 발생하며, 이는 바람직하지 않습니다.
- LLVM을 빌드 시간 종속성으로 유지하는 현재 상태 유지 (Maintain the status quo with LLVM as a build-time dependency): LLVM을 빌드 시간 종속성으로 유지하는 것은 채택 및 기여에 대한 기존 장벽을 유지하는 것입니다. 궁극적으로 이 옵션은 접근성 및 단순성의 핵심 과제를 해결하지 못하며, 작년 가을 Python 핵심 개발자 스프린트에서 바람직하지 않다고 여겨진 종속성(이 PEP의 발단)을 제거하지 못하므로 장기적인 해결책으로는 부적합합니다.
각주 (Footnotes)
이 Gist를 사용하여 계산되었습니다. 이 스크립트는 약 지난 90일 동안의 커밋을 재생하고, 각 커밋에 대해 플랫폼용 스텐실 파일을 생성한 다음, 변경 사항이 있는 경우 스텐실 파일을 저장소 사본에 커밋합니다. 이 계산은 git gc --aggressive
실행 전후의 저장소를 비교하며, 이는 저장소를 압축하는 데 사용됩니다 (GitHub가 저장소 복제 시 수행하는 것과 유사함).
저작권 (Copyright)
이 문서는 퍼블릭 도메인 또는 CC0-1.0-Universal 라이선스 중 더 관대한 라이선스에 따라 제공됩니다.
⚠️ 알림: 이 문서는 AI를 활용하여 번역되었으며, 기술적 정확성을 보장하지 않습니다. 정확한 내용은 반드시 원문을 확인하시기 바랍니다.
Comments