13P by GN⁺ 2일전 | ★ favorite | 댓글 1개
  • Python 패키지 관리자 uv는 pip보다 10배 이상 빠른 설치 속도를 보이며, 이는 단순히 Rust로 작성되었기 때문이 아니라 설계상의 선택에서 비롯됨
  • 속도를 가능하게 한 핵심은 정적 메타데이터 표준(PEP 518, 517, 621, 658) 으로, 코드 실행 없이 의존성을 파악할 수 있게 함
  • uv는 pip이 유지하는 레거시 기능(.egg, pip.conf, 시스템 Python 설치 등) 을 과감히 제거해 불필요한 코드 경로를 없앰
  • Rust가 기여한 부분은 제로-카피 역직렬화, 락 없는 동시성, 단일 바이너리 구조 등으로, 전체 속도 향상 중 일부만 차지함
  • 전체적으로 uv의 사례는 표준화된 메타데이터와 불필요한 호환성 제거가 성능 혁신의 핵심임을 보여줌

uv의 속도를 가능하게 한 표준

  • pip의 느림은 구현 문제가 아니라, 과거 setup.py 기반 구조로 인해 의존성을 알기 위해 코드를 실행해야 했던 구조 때문임
    • setup.py 실행에는 빌드 의존성 설치가 필요했고, 이는 “닭과 달걀 문제” 를 초래
    • 설치 과정에서 임의 코드 실행과 반복 실패가 발생, 설치 속도를 저하시킴
  • PEP 518(2016)pyproject.toml을 도입해 코드 실행 없이 빌드 의존성을 선언 가능하게 함
  • PEP 517(2017) 은 빌드 프런트엔드와 백엔드를 분리, pip이 setuptools 내부를 이해할 필요를 제거
  • PEP 621(2020)[project] 테이블을 표준화해 TOML 파싱만으로 의존성 확인 가능
  • PEP 658(2022) 은 패키지 메타데이터를 Simple Repository API에 직접 포함시켜, 휠 다운로드 없이 의존성 정보를 가져올 수 있게 함
  • PyPI는 2023년 5월 PEP 658을 적용했고, uv는 2024년 2월 출시되어 새 표준 인프라를 완전히 활용한 첫 도구로 등장
  • Rust의 Cargo나 npm처럼, Python 생태계도 이제 정적 메타데이터 기반 패키징으로 전환됨

uv가 제거한 기능들

  • uv의 속도는 불필요한 기능 제거에서 비롯됨
    • .egg 지원 없음: pip은 여전히 처리하지만 uv는 완전히 배제
    • pip.conf 무시: 설정 파일, 환경 변수, 상속 로직을 모두 생략
    • 바이트코드 컴파일 비활성화: .py.pyc로 변환하지 않아 설치 시간 단축
    • 가상환경 필수화: 시스템 Python에 직접 설치하지 않아 권한 검사와 안전성 코드 제거
    • 엄격한 스펙 준수: 잘못된 패키지를 거부해 예외 처리 로직 축소
    • requires-python 상한 무시: python<4.0 같은 방어적 제약을 무시해 의존성 해석(backtracking) 감소
    • 첫 번째 인덱스 우선: 여러 인덱스 중 첫 번째에서 패키지를 찾으면 즉시 중단, 의존성 혼동 공격 방지 및 네트워크 요청 절감
  • 이 모든 항목은 pip이 수행해야 하는 코드 경로를 uv가 제거한 사례임

Rust 없이 가능한 최적화

  • uv의 속도 중 상당 부분은 언어와 무관한 설계 최적화에서 비롯됨
    • HTTP Range 요청으로 휠 파일의 중앙 디렉터리만 부분 다운로드, 전체 파일 다운로드를 피함
    • 병렬 다운로드로 여러 패키지를 동시에 가져옴
    • 글로벌 캐시와 하드링크를 사용해 동일 패키지를 여러 가상환경에 설치해도 디스크 공간 추가 소모 없음
    • Python 비의존적 해석: TOML과 휠 메타데이터를 직접 파싱, setup.py만 있는 경우에만 Python 실행
    • PubGrub 의존성 해석 알고리듬 사용으로 pip의 백트래킹 방식보다 빠르고 오류 설명이 명확함

Rust가 실제로 기여한 부분

  • Rust는 특정 저수준 최적화에서 중요한 역할을 함
    • rkyv 기반 제로-카피 역직렬화로 캐시 데이터를 복사 없이 직접 사용
    • 락 없는 동시성 구조체로 안전한 병렬 접근 구현
    • 인터프리터 초기화 없음: uv는 단일 정적 바이너리로, pip의 Python 프로세스 생성 비용 제거
    • 버전 정보를 u64 정수로 압축 표현, 비교 및 해시 연산을 빠르게 수행
  • 이러한 요소들은 성능을 높이지만, 전체 속도 향상의 주된 원인은 아님

핵심 교훈

  • uv의 속도는 Rust 때문이 아니라, 하지 않는 것들 덕분
  • PEP 518·517·621·658의 표준화가 빠른 패키지 관리의 기반을 마련했고, uv는 레거시 제거와 현대적 가정으로 이를 실현
  • pip도 병렬 다운로드, 글로벌 캐시, 메타데이터 기반 해석을 구현할 수 있지만, 15년간의 하위 호환성 유지가 장애 요인
  • 결과적으로 pip은 항상 느릴 수밖에 없고, 새로운 전제에서 출발한 도구만이 근본적 속도 향상 가능
  • 다른 패키지 관리자에게 주는 교훈은, 정적 메타데이터·코드 실행 없는 의존성 탐색·사전 해석 가능 구조가 필수라는 점임
  • Cargo와 npm은 이미 이 방식을 채택하고 있으며, 의존성 확인을 위해 코드를 실행해야 하는 생태계는 근본적으로 느림
Hacker News 의견들
  • 이 글이 uv의 성능을 다각도로 잘 설명했다고 생각함
    Rust로 작성된 점도 도움이 되지만, 지난 10년간 Python 생태계가 setup.py 의존에서 벗어나도록 만든 표준화 노력이 훨씬 큰 역할을 했음

    • 예전에 Haskell 프로젝트를 진행할 때, 언어 자체보다 전문가 커뮤니티를 선별할 수 있다는 점이 장점이었음
      Rust도 비슷하게 커뮤니티의 역량을 끌어올리는 이유로 선택할 만함
    • 많은 Rust 리라이트 프로젝트가 이런 후광 효과를 얻음
      기존의 시행착오를 되짚으며 더 나은 설계를 할 수 있고, Rust 자체의 장점도 더해져 일종의 ‘원투 펀치’가 됨
  • “uv는 Rust라서 빠른 게 아니라, 하지 않는 일이 많아서 빠르다”는 주장에 공감함
    다만 벤치마크 없이 속도 요인을 단정하는 건 이르다고 봄
    PEP 518, 517, 621, 658의 영향은 크지만, 호환성 제거가 얼마나 기여했는지는 의문임
    또 언어 선택이 최적화에 어떤 영향을 줬는지도 다뤄지지 않음
    Cargo의 TOML 파서가 Python보다 훨씬 빠르다는 점도 흥미로움

    • pip의 과거 버전과 현재 버전을 비교하는 것도 일종의 벤치마크가 될 수 있음
      실제로 TOML은 빌드 시에만 읽히므로 큰 비중은 없지만, wheel 보급이 속도 향상에 기여했음
      관련 참고글: setup.py deprecated, wheels are faster
  • rkyv를 이용한 zero-copy deserialization은 Rust만의 기술은 아님
    C/C++ 같은 저수준 언어에서도 가능함

    • 여기서 “Rust 전용”이라 한 건 “Python에서는 불가능하다”는 의미로 이해함
      “인터프리터 시작이 없다”는 것도 같은 맥락임
    • Python으로는 zero-copy를 구현하기 어렵기 때문에, Rust가 이를 안전하고 쉽게 만든 점은 인정함
    • 결국 이 논의는 Rust 대 Python의 비교임
  • 글의 내용은 좋지만, LLM이 다듬은 문체가 너무 인공적으로 느껴짐
    언젠가는 LLM으로 인해 망가진 글을 다시 인간적으로 복원하는 시대가 올지도 모름

    • 작성자는 SBOM과 Lockfile 관련 글로도 HN에 올라온 적 있음
      공급망 보안 전문가로 보이지만, 그 글도 LLM 특유의 모호한 문체로 변질된 느낌이었음
    • 반대로 어떤 사람은 LLM 냄새를 전혀 못 느끼고 자연스럽다고 생각했음
    • 나는 이제 글에서 AI 냄새가 나면 바로 닫음
      고정된 프롬프트가 모든 글을 비슷하게 만들어버려서, 인터넷이 전부 같은 목소리로 들리는 게 아쉬움
  • uv의 속도에 열광하는 분위기가 이해되지 않음
    대부분의 Python 사용자는 패키지 설치 속도를 상위 10개 고민거리에도 두지 않을 것 같음
    나도 매일 Python을 쓰지만 체감이 크지 않음

    • 예전 회사에서는 poetry로 의존성 업데이트에 5~30분이 걸렸음
      실패라도 하면 다시 30분 기다려야 했는데, uv는 정말 쾌적한 경험이었음
    • 20년 넘게 Python을 써왔는데, pip install이 배포 시간의 큰 비중을 차지했음
      캐싱으로 속도를 높이려 애쓴 시간이 많았음
    • 내 업무용 모놀리식 앱에서 poetry install은 2분, uv sync는 몇 초면 끝남
      CI마다 2분씩 절약되니 누적 효과가 큼
    • uvx sometool을 실행할 때도 가상환경 생성과 의존성 설치가 몇 초면 끝나서 작업 흐름이 완전히 바뀜
    • 오랜 Python 사용자로서 uv의 속도는 “삶의 질 변화급”임
      이제 uv 없는 프로젝트로 돌아가기가 힘듦
  • uv의 일부 속도 최적화 기법은 pip에도 이식 가능해 보임
    예: 병렬 다운로드, .pyc 지연 생성, egg 무시, 버전 체크 등
    하지만 uv가 venv를 너무 잘 처리해서 굳이 pip를 손볼 필요는 없을 듯함
    결국 “Rust 덕분만은 아니다”라는 점에서 pip도 더 빨라질 여지가 있음

  • 빠른 언어를 선택하는 프로그래머는 이미 성능 최적화 마인드셋을 가진 경우가 많음
    언어 자체보다 그런 태도가 성능을 좌우함

  • uv가 python<4.0 상한을 무시하는 이유가 흥미로움
    대부분의 패키지가 Python 4에서 깨질까 봐 방어적으로 설정한 것이지, 실제로는 문제없음
    상한 제한은 현실적인 문제보다 가상의 문제를 해결하려는 시도였음

    • uv가 모든 상한을 무시하는 건 아니고, 4.0 한정으로 무시하는 것 같음
      python<3.0 같은 제약은 여전히 중요하므로, 그런 경우는 막아야 함
  • PEP 658이 2023년에 적용되고 uv가 2024년에 등장한 건 우연이 아님
    생태계가 준비되었기에 uv 같은 도구가 가능해졌음
    그런데 패키지 유지보수자들이 왜 이런 변화를 받아들였는지 궁금함
    setup.py가 잘 작동하던 사람들도 있었을 텐데, pyproject.toml로 옮긴 동기가 무엇이었을까

    • 사실 setup.py는 많은 사람에게 불편했음
      예를 들어 Requests조차 아직 완전한 PEP 517/518/621 호환이 아님
      1년 반이 지나도 minor 릴리스가 지연되고, 그 사이 빌드 문제도 발생했음
    • 정적 선언이 더 안전하고 성능상 유리했기 때문임
      다만 pip가 왜 이를 충분히 활용하지 않는지는 의문임
  • “하지 않는 코드 경로는 기다릴 필요가 없다”는 표현은 부정확함
    실행하지 않는 코드만이 시간을 절약함
    예를 들어 .egg 지원이 없어도 이미 폐기된 포맷이라면 속도에 영향이 없음
    실제로 어떤 항목이 얼마나 시간을 절약했는지 정량적 데이터가 있으면 더 좋았을 것임
    그래도 전반적으로 잘 정리된 글임