🔥 uv pip install torch 한 줄이면 끝 — Wheel Variants 실전
강의 목차

세 줄이면 된다.
curl -LsSf https://wheelnext.astral.sh | sh
uv venv
uv pip install torchcurl -LsSf https://wheelnext.astral.sh | sh
uv venv
uv pip install torch이 세 줄을 실행하면 variant 지원이 포함된 실험적 uv 빌드가 설치되고, 내 GPU에 맞는 CUDA 버전의 PyTorch가 자동으로 깔린다. 인덱스 URL을 직접 설정할 필요가 없다. CUDA 버전을 미리 확인할 필요도 없다. 인스톨러가 알아서 감지하고 적절한 wheel을 골라준다.
Part 1에서 Python 패키징이 2009년 CPU에 멈춰 있다는 문제를 다뤘다. Part 2에서는 그 문제를 실제로 해결하고 있는 도구와 생태계를 본다.
작동하는 프로토타입
에피소드에서 NVIDIA의 Jonathan Dekhtiar가 free-threaded Python의 Sam Gross를 인용한 이유가 있다. "우리도 보여주기로 했다. 말만으로는 사람들이 '리졸버가 너무 느려질 거야'라고 반박할 것이다. 그래서 직접 만들어 증명했다."
1년간의 프로토타이핑 과정에서 Wheel Next 팀은 Python 패키징 생태계 전체를 포크했다. pip, uv, Warehouse(PyPI의 백엔드), packaging 라이브러리, setuptools, scikit-build-core, Meson Python. 문자 그대로 패키징 인프라의 모든 모서리에 손을 대야 했다. 물론 목표는 언포크, 즉 변경사항을 원래 프로젝트에 다시 합치는 것이다.
Astral 쪽에서는 Constantine(uv 팀 멤버)이 variant 지원 uv 브랜치를 구현했다. 별도 URL(wheelnext.astral.sh)로 배포되는 이 빌드는 실제로 PyTorch의 CPU 빌드와 NVIDIA CUDA 빌드를 자동으로 구분해서 설치할 수 있다. 실험적이지만 작동한다.
어떻게 작동하나: variant provider 플러그인
구조를 좀 더 뜯어보자. Wheel Variants 시스템의 핵심은 "variant provider" 플러그인이다.
패키지 제작자가 wheel 파일에 variant 메타데이터를 넣는다. "이 wheel은 CUDA 12.6 이상이 필요합니다"라는 식이다. 인스톨러(uv 같은)는 설치 시점에 variant provider를 실행한다. NVIDIA variant provider는 사용자 머신의 GPU 드라이버 버전을 확인하고, 그 드라이버가 지원하는 CUDA 런타임 버전을 알려준다. 인스톨러는 이 정보를 wheel의 variant 메타데이터와 대조해서 호환되는 빌드를 고른다.
같은 구조가 CPU 명령어 세트에도 적용된다. CPU variant provider는 현재 머신의 CPU가 지원하는 명령어(AVX2, AVX-512 등)를 감지하고, 인스톨러는 가장 최적화된 wheel을 선택한다.
이 설계에서 variant는 독립적으로 관리되는 네임스페이스별로 분류된다. CUDA variant는 NVIDIA가 관리하고, CPU variant는 별도로 관리하고, 미래에 새로운 하드웨어 축이 등장하면 새 네임스페이스를 추가하면 된다. 인스톨러나 PyPI를 수정할 필요 없이 확장되는 구조다.
Charlie Marsh의 표현이 정확했다. "우리가 하려는 건, 사용자가 어떤 하드웨어 설정을 하고 있든 uv pip install torch만 치면 되게 만드는 것이다. 나머지는 전부 자동으로 처리되어야 한다."
pyx: 표준이 바뀌기 전에도 문제를 풀다
Astral은 두 트랙으로 접근하고 있다. 하나는 PEP를 통한 표준 변경(장기), 다른 하나는 지금 당장 사용자를 돕는 것(단기). 단기 트랙의 결과물이 pyx다.
pyx는 Astral이 운영하는 호스티드 패키지 레지스트리로, 2025년 8월부터 베타 상태다. 핵심 기능 중 하나가 GPU 인식 패키지 관리다. PyTorch 같은 패키지를 다양한 CUDA 버전, PyTorch 버전, Python 버전, CPU 아키텍처 조합으로 미리 빌드해서 제공한다.
표준이 바뀌지 않은 상태에서도 사용자가 직접 빌드 매트릭스를 신경 쓰지 않아도 되게 하겠다는 전략이다. Charlie는 "우리는 표준이 바뀌기를 기다리면서 동시에 지금 당장 삶을 좀 더 편하게 만들려고 한다"고 설명했다.
pyx가 Wheel Variants와 다른 점은, pyx는 현재 표준 안에서 작동하는 서비스이고 Wheel Variants는 표준 자체를 바꾸는 작업이라는 것이다. 둘은 경쟁이 아니라 보완 관계다.
Python Build Standalone: 또 하나의 레버
에피소드에서 흥미로웠던 순간이 Jonathan과 Charlie 사이의 교환이었다. Jonathan이 물었다. "Python Build Standalone을 다른 CPU 확장으로 빌드하면 얼마나 성능이 달라질까?"
Python Build Standalone은 Astral이 관리하는 프로젝트로, CPython을 다운로드해서 바로 실행할 수 있는 재배포 가능한 형태로 빌드한다. uv로 Python을 설치하면 이 빌드가 깔린다. Bazel을 포함한 여러 도구에서도 쓰인다. 7천만 회 이상 다운로드됐다.
Charlie는 "우리 목표는 가장 빠른 Python 배포판을 만드는 것이다. CPython 소스를 바꾸지 않고 빌드 방식만 바꿔서"라고 말했다. PGO(Profile-Guided Optimization)와 LTO(Link-Time Optimization)를 적용하고 있다고 한다. "아직 엄밀한 벤치마크를 공개하지는 않았지만, 지금 가장 빠른 Python이라고 생각한다."
이게 Wheel Variants와 무슨 관계냐면, Python Build Standalone도 결국 CPU 아키텍처별 최적화 빌드의 문제이기 때문이다. 지금은 wheel로 배포하지 않지만, 언젠가는 같은 variant 메커니즘으로 "이 Python 빌드는 AVX2 최적화 빌드입니다"라고 표시할 수 있게 될지 모른다.
Charlie 자신도 이 연결고리를 에피소드에서 처음 인식한 듯했다. "Jonathan이 이걸 언급하기 전까지 이게 레버라는 걸 깊이 생각해보지 않았다." uv가 전 세계 수많은 개발자의 Python 설치를 담당하고 있다는 건, Python Build Standalone의 최적화가 그 모든 개발자에게 직접 전달된다는 뜻이다.
문제를 먼저 정의한 사람
Ralf Gommers는 2010년부터 Python 패키징의 네이티브 코드 문제를 지켜봤다. NumPy 릴리스 매니저가 되면서 직접 겪기 시작했다. 2010년 당시 NumPy는 .exe 인스톨러를 PyPI에 올렸는데, 파일명에 _sse2, _sse3를 붙여서 사용자가 직접 골라 설치하게 했다. Linux에서 Wine으로 Windows 인스톨러를 빌드했다고 한다. 첫 릴리스를 내는 데 3개월이 걸렸다.
12~13년간 같은 논의가 메일링 리스트에서 반복되는 걸 지켜본 뒤, Ralf는 pypackaging-native 가이드를 만들었다. 핵심 원칙이 있다. "솔루션을 제안하지 않는다. 문제만 설명한다." SIMD 확장, GPU 패키징, 소스-바이너리 혼합 배포의 어려움. 다음에 누군가 같은 토론을 시작하면 이 사이트를 링크하고 "여기가 문제 정의입니다, 솔루션 이야기를 합시다"라고 말할 수 있게 하려는 것이다.
Jonathan은 이 사이트를 "인터넷 어디에서도 찾을 수 없는 최고의 패키징 문제 설명"이라고 평가했다. Wheel Next는 의도적으로 반대편에 섰다. "문제는 이미 설명되어 있다. 솔루션만 제안하자."
이 구분이 프로젝트 성공의 핵심 중 하나였다고 생각한다. 해결하려는 문제에 대해 14개 이상의 회사가 동의하고 있으니까, 솔루션 논의에 집중할 수 있었다.
이게 바꿀 것들
Wheel Variants가 채택되면 가장 즉각적인 수혜자는 과학 계산 생태계다.
SciPy는 이미 갖고 있는 AVX2, ARM Neon 코드를 배포할 수 있게 된다. 빌드 옵션 하나만 바꾸고 별도 wheel을 생성하면 끝이다. 코드는 이미 있다. 배포 수단만 없었다.
scikit-learn, Pandas, Pillow도 마찬가지다. SIMD 최적화 코드를 작성할 동기가 생긴다. 만들어도 배포할 방법이 없었으니 안 만든 건데, 방법이 생기면 이야기가 달라진다.
PyTorch는 900MB짜리 Fatbin에서 해방된다. CUDA 아키텍처별로 wheel을 나눌 수 있으니까 200~250MB 수준으로 줄어든다. 사용자는 퍼즐북 같은 설치 가이드를 읽을 필요가 없어진다. uv pip install torch. 끝.
PyPI 인프라 측에서도 대역폭 절감 효과가 크다. 하나의 거대한 wheel 대신 여러 작은 variant wheel을 서빙하면 필요한 것만 전송하면 된다.
물론 트레이드오프가 있다. CI 서버에서 빌드해야 할 wheel 수가 늘어난다. variant별로 별도 빌드를 돌려야 하니까. 하지만 Jonathan이 말한 대로, "한 번 빌드하면 백만 번 설치된다." 빌드 비용의 일회성 증가와 배포 비용의 지속적 감소를 비교하면 명확한 트레이드오프다.
앞으로의 과정
PEP 825가 Draft 상태이므로 아직 갈 길이 있다. 커뮤니티 리뷰를 거쳐야 하고, pip, Poetry, Hatch, PDM 같은 다른 인스톨러 개발자들의 동의가 필요하다. 빌드 백엔드(setuptools, scikit-build-core, Meson Python)와 인덱스 서버(PyPI/Warehouse)도 변경해야 한다. 후속 PEP 3~4개가 더 나와야 한다.
Ralf는 PEP 825가 아마 "잠정 수락(provisionally accepted)" 상태가 될 거라고 예상했다. 나머지 조각들이 전부 맞물려야 최종 수락이 가능하니까.
하지만 이미 작동하는 프로토타입이 있다는 건 큰 차이다. PyTorch 2.8에서 Wheel Variants 실험적 지원이 제공되고 있다. uv의 variant 빌드로 테스트할 수 있다. NVIDIA 블로그, Quansight 블로그, Astral 블로그에서 각자의 관점이 공개되어 있다. 투명하다.
이 에피소드를 듣고 나서 드는 생각은, Python 패키징 문제가 결국 "충분히 많은 사람이 동시에 신경 써야 풀리는 문제"였다는 거다. NumPy 팀 혼자서 CPU dispatcher를 만들어도, PyTorch 팀 혼자서 별도 인덱스를 관리해도, 근본 문제는 안 풀린다. wheel 스펙 자체가 바뀌어야 하고, 인스톨러가 바뀌어야 하고, 레지스트리가 바뀌어야 하고, 빌드 도구가 바뀌어야 한다. 전부 동시에.
14개 이상의 회사가 모여서 1년 동안 프로토타입을 돌리고 PEP를 쓰고 커뮤니티 리뷰를 진행하는 이 과정이 인상적이다. "문제 잘 정의하면 절반은 해결"이라는 말이 있는데, Ralf의 pypackaging-native가 문제를 정의했고, Wheel Next가 나머지 절반을 채우고 있다.
아직 완성은 아니다. PEP 프로세스가 느리고, 모든 도구가 동시에 업데이트되어야 하니까 시간이 걸릴 것이다. 하지만 방향은 맞다고 확신한다. uv pip install torch가 그냥 되는 세상. 그게 오고 있다.
참고 자료
- Talk Python To Me #544: Wheel Next + Packaging PEPs — 이 글의 원본 에피소드
- Astral: An experimental, variant-enabled build of uv — variant 지원 uv 빌드 안내 및 데모
- NVIDIA: Streamline CUDA-Accelerated Python Install and Packaging Workflows with Wheel Variants — NVIDIA의 Wheel Variants 활용 가이드
- PyTorch: Wheel Variants, the Frontier of Python Packaging — PyTorch 2.8의 Wheel Variants 실험적 지원
- Quansight: Python Wheels: from Tags to Variants — Quansight의 기술적 관점 정리
- pyx: a Python-native package registry — Astral의 GPU 인식 패키지 레지스트리
- pypackaging-native — Python 네이티브 코드 패키징 문제를 정의한 가이드
시리즈: Python Wheel Variants — 패키징의 하드웨어 인식 혁명










