3P by GN⁺ | ★ favorite | 댓글 1개
  • bzip2 crate 0.6.0은 bzip2 알고리듬의 Rust 구현인 libbz2-rs-sys를 기본값으로 채택해 C 의존을 제거하고, 속도와 교차 컴파일 편의성을 함께 개선함
  • bzip2는 사용 빈도가 줄어든 오래된 알고리듬이지만, 여러 프로토콜과 라이브러리가 명세 준수를 위해 여전히 지원해야 해 의존성 트리 깊은 곳에 남아 있음
  • Rust 구현은 압축에서 C 대비 약 9.66~14.87% 적은 CPU 사이클을 사용했고, 압축 해제에서도 테스트 전반에서 4.48~10.00% 개선됨
  • C 제거로 WebAssembly, Windows, Android 빌드가 단순해지고, 기본적으로 libbz2-rs-sys 심볼을 내보내지 않아 다른 의존성과의 심볼 충돌 위험도 줄어듦
  • 감사에서는 off-by-one 논리 버그 1건과 퍼저 제한 일부가 수정됐으며, bzip2를 쓰는 상위 라이브러리와 애플리케이션도 MIRI 실행이 가능해짐

bzip2 0.6.0의 기본 구현 변경

  • bzip2 crate 0.6.0은 bzip2 알고리듬의 Rust 구현인 libbz2-rs-sys를 기본으로 사용함
  • 기존 C 의존을 제거하면서 bzip2 crate는 더 빠르고 교차 컴파일하기 쉬워짐
  • libbz2-rs-sys crate는 C 프로젝트에서도 개선 효과를 활용할 수 있도록 C 동적 라이브러리로 빌드 가능함
  • bzip2는 오늘날 사용량이 많지 않지만, 많은 프로토콜과 라이브러리가 명세 준수를 위해 여전히 지원해야 하며 여러 프로젝트의 의존성 트리 깊은 곳에 남아 있음
  • 구현 세부사항은 이전 글 Translating bzip2 with c2rust에서 확인할 수 있음

성능 개선 결과

  • Rust 구현은 일반적으로 C 구현보다 빠르며, 일부 경우에는 C 성능과 비슷한 수준임
    • 알려진 범위에서 실질적으로 더 느린 사례는 없음
  • 압축 벤치마크에서는 C 대비 CPU 사이클이 줄어듦
    • sample3.ref level 1: 38.51M33.53M, -14.87%
    • silesia-small.tar level 1: 3.43G3.00G, -14.30%
    • silesia-small.tar level 9: 3.47G3.17G, -9.66%
    • bzip2의 level은 작업 메모리 사용량을 뜻하며 성능에는 큰 영향을 주지 않음
    • sample3.ref는 level 1만으로도 파일 크기보다 많은 메모리를 할당하므로 더 높은 level은 관련성이 낮음
  • 압축 해제 성능도 테스트 전반에서 개선됨
    • sample3.bz2: -4.48%
    • sample1.bz2: -8.63%
    • sample2.bz2: -7.67%
    • dancing-color.ps.bz2: -5.17%
    • re2-exhaustive.txt.bz2: -7.65%
    • zip64support.tar.bz2: -10.00%
  • macOS 벤치마크 머신에서는 압축 해제 성능 수치가 가끔 낮게 나오는 경우가 있음
    • 원인은 확인되지 않았고, macOS에서는 perf처럼 성능 추적을 자동화할 수 있는 도구를 작동시키기 어려웠음

빌드와 심볼 충돌 완화

  • C 의존성이 있는 Rust 프로젝트의 교차 컴파일은 cc crate 덕분에 바로 동작하는 경우가 많지만, 실패하면 오류 디버깅이 어려울 수 있음
    • 시스템 라이브러리 링크도 혼란스럽고 재현하기 어려운 문제를 만들 수 있음
    • bzip2의 WebAssembly 컴파일은 오래전부터 문제가 있었음
    • C 의존을 제거하고 Rust 코드만 사용하면서 WebAssembly, Windows, Android 빌드가 동작하기 쉬워짐
  • libbz2-rs-sys는 기본적으로 심볼을 export하지 않음
    • C 의존성을 사용하면 Rust extern 블록이 찾을 수 있도록 심볼을 export해야 함
    • export된 이름은 다른 의존성이 같은 심볼을 선언할 때 충돌할 수 있음
    • Rust 프로젝트가 심볼 export를 필요로 하면 기능 플래그로 활성화할 수 있음

검증과 감사 결과

  • 성능을 내는 bzip2 구현에는 일부 unsafe 코드가 필요하고, C 인터페이스를 Rust로 복제하려면 더 많은 unsafe 코드가 들어감
    • 해당 코드는 MIRI에서 실행 가능함
    • bzip2를 사용하는 고수준 라이브러리나 애플리케이션도 이제 MIRI로 실행할 수 있음
  • 감사에서는 off-by-one 논리 버그 1건이 발견됐고 퍼저 제한 일부가 수정됨
  • 이번 작업은 bzip2 crate의 유지관리자인 Alex Crichton, 감사와 전문성을 제공한 Radically Open Security, e-Commons Fund를 통해 자금을 지원한 NLnet Foundation의 도움을 받음

댓글과 토론

Hacker News 의견들
  • Trifecta Tech 구현이 2019년 이후 업스트림 릴리스가 없는 리눅스 배포판의 “공식” 구현을 대체하기 시작할 가능성이 얼마나 현실적인지 궁금함
    Fedora가 최근 원래의 Adler zlib 구현을 zlib-ng로 바꿨으니 불가능한 일은 아님
    원래 구현과 호환되는 C ABI만 제공하면 됨

    • 2019년 이후 업스트림 릴리스가 없다면 그 구현이 그냥 완성된 상태라는 뜻일 수도 있지 않나 싶음
      더 고칠 버그도, 추가할 기능도 없을 수 있고, 그렇다면 딱히 문제가 없어 보임
    • Ubuntu가 Rust sudo를 쓰고 있으니 충분히 가능함
    • 이미 호환되는 C ABI를 제공하고 있음
      누군가가 “그냥” 실제로 배포판에 적용되도록 작업하면 됨
    • 그게 uutils의 목표라고 봄
      https://uutils.github.io/
    • 잠깐 살펴보니 이미 cargo-c 설정이 있어서 좋지만, 현재는 이름공간이 달라서 C 프로그램이 libbz2로 자동 감지하지는 못함
      https://github.com/trifectatechfoundation/libbzip2-rs/blob/8...
      bzip2 심볼을 충분히 잘 몰라서 ABI 호환성은 말하기 어려움
      이런 걸 탐구하는 장난감 프로젝트가 있지만, GNU 운영체제 구현을 유지보수하는 데 필요한 시간을 내기가 어렵고 풀 리퀘스트는 환영함
      https://github.com/kpcyrd/platypos
  • 이 크레이트로 Common Crawl 수백 TB를 처리하고 있어서, 속도 개선이 반가움

    • 여기서 bz2를 쓰는 이유가 궁금함
      한 번 zstd로 변환해두는 편이 더 빠르지 않을까 싶음
      내가 알기로 높은 압축 수준에서는 zstd가 모든 지표에서 bzip2를 이김
    • 이 데이터가 토렌트로도 제공되는지 궁금함
    • 압축 속도 14% 개선이면 꽤 좋음
  • 이게 기본적으로 남아 있는 CVE 11개를 해결하는지 궁금함
    아이러니하게도 bzip2 크레이트에도 CVE가 하나 보고되어 있음
    [1] https://app.opencve.io/cve/?product=bzip2&vendor=bzip2_proje...

    • 그 크레이트에 보고된 “큰 파일 때문에 런타임 실패”와 C에서 흔한 “경계 검사 누락”들은 확실히 대비됨
      경계 검사 누락을 악용해 코드 실행까지 이어가려는 시도가 얼마나 있었는지 궁금함
      그런 권한 상승이 가능할 수도, 불가능할 수도 있음
    • “0.4.4 이전의 bzip2 크레이트”라고 되어 있는데, 오늘 0.6.0을 릴리스하는 중임 :>
  • 본문에 “오늘날 거의 쓰이지 않는 90년대 알고리즘을 왜 붙잡고 있나?”라고 되어 있는데, 요즘은 뭐가 쓰이는지 궁금함
    zstd인가?
    아, 이걸 봤음: https://quixdb.github.io/squash-benchmark/

  • C 버전과 Rust 버전에 같은 최적화 옵션의 LLVM 코드 생성 백엔드를 쓰는지 궁금함
    그렇다면 속도 향상이 어디서 나오는지도 궁금함
    예를 들면 Rust의 자동 SIMD 같은 것인지, 재작성 기회에 다른 부분을 손으로 최적화한 것인지, 더 최신의 최적화된 라이브러리를 쓰는 것인지, 아니면 다른 이유인지

    • 추측하자면 Rust가 코드 생성기에 더 많은 힌트를 넘겨줄 수 있음
      예를 들어 C 포인터보다 별칭 문제를 덜 신경 써도 됨
      https://en.wikipedia.org/wiki/Aliasing_(computing)#Conflicts...
    • C는 현대적인 고성능 코드를 쓰기엔 솔직히 꽤 나쁜 언어임
      C99와 C21 사이 약 20년 동안, 새로 추가된 많은 명령어를 관용적으로 대상으로 삼는 데 필요한 기능이 언어에 추가되지 않았음
      clz, popcnt, clmul, pdep 같은 추상 기계 명령을 제대로 얻는 것만으로도 이런 코드를 작성할 때 큰 도움이 됨
    • 어떤 언어 X, Y, Z로 재작성하든 속도를 높일 기회가 생김
      Rust 자체에만 내재된 건 없음
  • 이들이나 Prossimo가 BGP, OSPF, RIP 같은 핵심 인터넷 프로토콜, 다른 라우팅 구현, DNS 서버 등도 비슷한 방식으로 다시 구현해줬으면 함

  • macOS에 perf가 없다는 얘기와 관련해, 프로파일링에는 dtrace만으로도 꽤 멀리 갈 수 있음
    원래 Perl로 된 flame graph 스크립트도 dtrace 사용을 언급하고, Rust 재구현도 dtrace를 사용함
    캐시 미스나 은퇴한 마이크로 명령 같은 지표는 없지만 그래도 매우 유용할 수 있음

  • Lbzip2는 사용 가능한 모든 CPU 코어를 써서 압축 해제 속도가 훨씬 빨랐음
    2025년인데도 Python 같은 많은 프로그램은 CPU 코어 하나에 묶여 있음

  • lbzip2 방식의 병렬 압축 해제를 지원하는지 궁금함
    아니면 블록 매직을 미리 스캔하는 반복자를 제공해서 그 위에서 병렬 압축 해제가 가능하게 하는지라도 궁금함
    수정: 아마 지원하지 않는 듯함