Bundler가 uv만큼 빠를 수 있을까?

1 month ago 14

  • Bundler의 성능 한계를 분석하며, Python의 패키지 관리자 uv가 빠른 이유를 비교
  • uv의 속도는 Rust 언어 때문이 아니라 병렬 다운로드, 글로벌 캐시, 메타데이터 기반 의존성 처리 등 구조적 설계 덕분임
  • Bundler는 다운로드와 설치 과정이 결합되어 있어 병렬 처리에 제약이 있으며, 이를 분리하면 큰 개선 가능
  • 글로벌 캐시 통합, 하드링크 설치, PubGrub 해석기 통합 등으로 RubyGems와 Bundler의 중복을 줄일 수 있음
  • 언어 재작성 없이도 대부분의 성능 향상은 Ruby 코드 내에서 달성 가능, uv 수준의 속도에 근접할 수 있음

Bundler와 uv의 성능 비교

  • RailsWorld에서 제기된 “왜 Bundler는 uv만큼 빠르지 않은가?”라는 질문을 계기로 Bundler의 성능 병목을 조사
  • 작성자는 Bundler가 uv 수준의 속도를 달성할 수 있다고 확신하며, 성능 차이는 언어가 아니라 설계의 문제라고 명시
  • Andrew Nesbitt의 글 *“How uv got so fast”*를 인용해 uv의 핵심 최적화 방식을 Bundler에 적용 가능 여부로 분석

Rust로의 재작성 여부

  • uv가 Rust로 작성된 점은 사실이나, 속도의 본질적 원인은 Rust 자체가 아님
  • Bundler의 병목을 제거해 “Rust로 다시 쓰는 것만이 남은 개선책”이 된다면 그것이 성공이라 평가
  • Rust 재작성은 기존 호환성 제약 없이 실험적 설계를 시도할 자유를 제공하지만, 필수 조건은 아님

Bundler의 구조적 병목

  • Bundler는 gem 다운로드와 설치를 하나의 메서드에 결합해 병렬 다운로드가 불가능
    • 예시 코드에서 install 메서드가 fetch_gem_if_not_cached와 install을 연속 실행
    • 이로 인해 의존 관계가 있는 gem(a -> b -> c)은 순차적으로만 설치됨
  • 실험 결과, 의존성이 있는 경우 9초 이상 소요되지만, 독립적인 gem(d, e, f)은 병렬 다운로드로 4초 내 완료
  • 다운로드와 설치를 분리하면 의존성 규칙을 유지하면서도 병렬 처리 가능
    • 네 단계(다운로드 → 압축 해제 → 컴파일 → 설치)로 분리 제안
    • 순수 Ruby gem은 의존성 설치 순서를 완화해 추가 속도 향상 가능

캐시 및 설치 최적화

  • uv의 글로벌 캐시와 하드링크 설치 방식을 Bundler에도 적용 가능
    • Bundler와 RubyGems는 현재 Ruby 버전별로 별도 캐시를 사용
    • $XDG_CACHE_HOME 기반의 공유 캐시로 통합 필요
    • 하드링크 설치는 캐시 통합 후 적용 가능
  • Bundler는 이미 PubGrub 의존성 해석기를 사용하지만, RubyGems는 여전히 molinillo를 사용
    • 두 시스템의 해석기 통합이 기술 부채 해소의 핵심

Rust 관련 최적화 요소의 적용 가능성

  • Zero-copy 역직렬화는 RubyGems의 YAML 파싱 단계에서 일부 적용 가능성
  • Ruby의 GVL(Global VM Lock) 은 IO 중심 작업에서는 병렬 처리에 큰 제약이 없음
    • IO와 ZLIB 처리는 GVL을 해제하므로 병렬 실행 가능
    • 단, 작은 파일 쓰기에서는 GVL 관리 오버헤드가 성능 저하 요인
    • Ruby 내부에서 이를 개선하는 작업이 진행 중
  • 버전 비교 최적화: uv는 버전을 u64 정수로 인코딩해 비교 속도를 높임
    • Ruby에서도 Gem::Version을 정수 기반으로 변환해 해석기 성능 향상 가능
    • 이미 관련 리팩터링 시도가 있었으나 하위 호환성 문제로 보류

결론 및 향후 계획

  • uv의 속도는 언어보다 불필요한 작업을 제거한 설계 덕분이며, Bundler도 같은 방향으로 개선 가능
  • RubyGems와 Bundler는 이미 현대적 패키지 관리 구조를 갖추고 있어, uv 수준의 속도 달성이 현실적
  • 가장 큰 과제는 레거시 코드와 호환성 유지
  • Rust로 재작성하지 않아도 99%의 성능 향상은 Ruby 코드 내에서 가능, 나머지 1%는 미미한 수준
  • 후속 글에서는 Bundler와 RubyGems의 실제 프로파일링과 구체적 병목 원인을 다룰 예정

Read Entire Article