bzip2 Crate가 C에서 100% Rust로 전환됨
(trifectatech.org)- bzip2 크레이트가 C 코드 의존성을 100% Rust 구현으로 대체함
- 성능이 기존보다 전반적으로 향상되고, 크로스 컴파일이 더 쉬워짐
- Rust 구현은 C 버전 대비 데이터 압축 및 해제 속도 모두 개선됨
- 심볼 충돌 문제와 같은 라이브러리 의존성 이슈가 크게 줄어듦
- 보안 감사를 거쳐 중요한 로직 버그를 수정, 안정성이 검증됨
bzip2 크레이트 0.6.0 출시 및 Rust 기반 전환
- 오늘 bzip2 버전 0.6.0이 배포되었음
- 이제 기본적으로 자체 개발한 rust 기반 bzip2 알고리듬 구현체인 libbz2-rs-sys을 사용함
- 이 전환을 통해 bzip2 크레이트는 더욱 빨라지고, 크로스 컴파일이 쉬워졌음
- libbz2-rs-sys 크레이트는 C 동적 라이브러리 형태로도 빌드 가능함. 이를 통해 C 프로젝트에서도 성능 개선을 활용할 수 있음
왜 이런 전환이 이루어졌는가?
- bzip2 알고리듬은 90년대에 만들어져 지금은 널리 쓰이지는 않지만, 여러 프로토콜과 라이브러리에서 여전히 규격 준수를 위해 필요함
- 많은 프로젝트가 직접적으로는 아니지만 의존성 트리 어느 깊은 곳에서 bzip2에 의존하고 있음
- 우리는 zlib-rs에서 쌓은 경험을 바탕으로 이번에 bzip2 구현을 현대화했음
- libbz2-rs-sys 구현 세부 내용은 이전 블로그 포스트에서 다룸. 여기서는 이번 전환의 이점을 살펴봄
향상된 성능
- rust 구현체는 전반적으로 C 버전보다 더 높은 성능을 보임
- 일부 상황에서는 동등한 성능이지만, 느린 경우는 없음
- 압축 성능: bzip2에서는 level 옵션이 있지만 성능 영향은 미미함
- 테스트 결과, 대표적인 샘플 파일에서 rust 버전이 10% 이상 속도 향상됨
압축:
파일 | C(수행 사이클) | Rust(수행 사이클) | 상대적 변화 |
---|---|---|---|
sample3.ref (level 1) | 38.51M | 33.53M | -14.87% |
silesia-small.tar (level 1) | 3.43G | 3.00G | -14.30% |
silesia-small.tar (level 9) | 3.47G | 3.17G | -9.66% |
압축 해제에서도 모든 경우에서 개선된 성능을 보여줌:
파일 | C(수행 사이클) | Rust(수행 사이클) | 상대적 변화 |
---|---|---|---|
sample3.bz2 | 2.53M | 2.42M | -4.48% |
sample1.bz2 | 9.63M | 8.86M | -8.63% |
sample2.bz2 | 20.47M | 19.02M | -7.67% |
dancing-color.ps.bz2 | 87.46M | 83.16M | -5.17% |
re2-exhaustive.txt.bz2 | 1.89G | 1.76G | -7.65% |
zip64support.tar.bz2 | 2.32G | 2.11G | -10.00% |
단, macOS 환경에서는 간혹 압축 해제 수치 변화가 발생함. 성능 측정 도구의 한계로 분석이 어려웠음
크로스 컴파일 지원
- C 의존성이 있는 Rust 프로젝트의 크로스 컴파일은 보통 cc 크레이트 덕분에 잘 동작하지만, 실패 시 디버깅이 매우 어려움
- 시스템 라이브러리 링크 과정에서 예기치 않은 문제가 발생하기 쉽고, WebAssembly 빌드를 비롯한 일부 환경에서는 실질적 장애 요소가 됨
- rust 구현으로 전환함으로써 C 관련 문제들이 완전히 사라짐
- 이제 윈도우, 안드로이드, 웹어셈블리 등에서도 특이사항 없이 크로스 컴파일이 가능함
- 이는 사용자 경험뿐 아니라 유지보수 관점에서도 큰 장점임
기본적으로 심볼(export) 충돌 없음
- C 의존성의 경우 Rust 외부 블록에서 심볼을 export해야 하기 때문에, 다른 의존성이 동일한 심볼을 export할 시 충돌이 발생함
- libbz2-rs-sys는 기본적으로 심볼을 export하지 않도록 설계됨
- 따라서 다른 외부 라이브러리와 심볼 충돌이 발생할 일이 없음. 필요하다면 feature flag로 export를 활성화할 수도 있음
MIRI 기반 테스트 실행
- Rust에서 bzip2를 성능 높게 구현하려면 unsafe 코드 사용이 불가피하고, C 인터페이스 복제에도 unsafe 코드가 다수 필요함
- 다행히 이 코드를 MIRI 환경에서 실행 및 테스트할 수 있음
- 더 나아가, bzip2를 사용하는 상위 레벨 라이브러리나 애플리케이션도 이제는 MIRI 테스트가 가능해짐
결론
이제 bzip2 크레이트는 더 빨라졌음. 더 이상 신경 쓰지 않아도 될 정도로, 자연스럽게 더 나은 경험을 제공함
Hacker News 의견
- Trifecta Tech의 구현체가 리눅스 배포판에서 사용하는 공식 구현을 대체하게 될 가능성을 생각해보면, 예전에 Fedora가 기존 Adler zlib에서 zlib-ng로 교체한 사례가 있다는 점에서 불가능하지 않다는 판단. 핵심은 원본과 호환되는 C ABI를 제공하면 된다는 의견
- 만약 2019년 이후로 업스트림 릴리즈가 없는 상황이라면, 이 구현체는 그냥 완성된 것이 아닌지에 대한 의문 제기. 더 이상 수정할 버그나 추가할 기능이 없다면, 그 자체로 충분하다는 생각
- Ubuntu가 Rust로 작성된 sudo를 사용하기 때문에 이런 교체는 충분히 가능성 있는 일이라는 생각
- Trifecta Tech에서 C ABI를 호환성 있게 잘 제공하고 있지만, 결국 누군가가 이 작업을 실제로 수행해야 변화가 일어난다는 의견
- uutils의 목표도 이런 공식 리플레이스먼트에 있다고 언급하며 uutils 홈페이지 링크 공유
- 간단히 살펴본 결과 이미 cargo-c 설정이 존재해 긍정적으로 보이지만, 네임스페이스가 다르기 때문에 C 프로그램에서 기존 libbz2로 자동감지가 되진 않는 상황. bzip2의 심볼에 익숙치 않아 정확한 ABI 호환 여부는 알기 어렵다는 점 언급. 직접 GNU 운영체제 구현체를 관리하는 것은 시간이 많이 소요되어 어렵고, 시간 날 때 실험적인 프로젝트인 platypos에서 PR 환영한다는 입장
- 나는 이 크레이트를 활용해 수백TB의 Common Crawl 데이터를 처리중. 속도가 빨라져서 매우 만족하는 입장
- bz2를 여기서 사용하는 이유에 대한 질문 제기. 대용량 변환을 한 번만 하려면 zstd로 전환하는 것이 이점이 크다고 들었고, 압축률이 높을수록 모든 면에서 bzip2보다 낫다는 근거 제시
- Common Crawl 데이터가 토렌트 형태로 공개되어 있는지 궁금하다는 질문
- 압축 속도 14% 개선은 꽤 훌륭하다는 감상 공유
- 이 구현체가 기본적으로 11개의 남아있는 CVE를 해결하는지 궁금. 아이러니하게도 bzip2 크레이트에도 CVE 신고가 있었다며 관련 링크 공유
- 해당 크레이트에서 보고된 ‘큰 파일에서 런타임 실패’류 취약점과, C로 작성된 경우의 ‘bounds miss’ 문제 간 대조가 흥미롭다는 의견. 이러한 bound miss 취약점이 실제로 코드 실행까지 이어질 수 있을지 궁금
- ‘bzip2 크레이트는 0.4.4 이전 버전에 취약점이 있다’는 안내 인용. 오늘 0.6.0 릴리즈라는 추가 정보
- ‘90년대 알고리즘을 왜 굳이 개선하냐’는 질문에 대해, 최근에는 어떤 알고리즘이 쓰이는지 궁금하다는 의견. zstd를 언급하며 압축 알고리즘 비교 벤치마크 링크 공유
- C와 Rust 각각의 컴파일러 코드 생성(코드젠) 백엔드가 동일하다면 어떻게 속도 향상이 발생하는지 궁금. Rust의 auto-simd나 직접 최적화, 혹은 새로운 최적화 라이브러리 활용 등 다양한 요인 가능성 제기
- 추측이지만, Rust는 더 많은 힌트를 코드 생성기에 제공할 수 있다고 봄. 예시로, C 포인터와 다르게 별칭(aliasing) 문제를 적게 걱정할 수 있다는 점. Aliasing 설명 링크 제시
- C 언어는 현대 고성능 코드 작성 측면에서 정말 좋지 않다는 의견. C99~C21까지 약 20년간 언어 자체가 클린한 방식으로 새로운 명령어(clz, popcnt, clmul, pdep 등)를 활용하기 위한 기능이 부족했다는 지적. 이런 추상화 명령어 지원만으로도 이러한 종류의 코드 최적화에 큰 도움을 준다고 평가
- 어떤 언어로든 다시 작성하면 속도 개선의 기회가 있고 Rust만의 고유한 속도 보장은 아니라는 의견
- 나나 Prossimo에서 핵심 인터넷 프로토콜(BGP, OSPF, RIP 등) 및 라우팅 구현체, DNS 서버 등을 비슷한 방식으로 재작성해주길 바라는 희망
- 최근 몇 년간 Rust 등 안전한 언어로 인터넷과 OS 핵심 도구 리라이팅을 지원하는 펀드, NLnet 프로젝트와 Sovereign Tech Fund 링크 소개. 예시로 BGP in Rust 프로젝트도 동반 언급
- Memory Safety Initiative에서는 TLS와 DNS 등 핵심 서비스의 안전한 리라이팅 노력을 소개하고 있어 내 제안과 일부 맥락이 일치
- 한 개발자가 SPARK Ada로 Ironsides DNS를 만들었는데, 이 언어는 더 강한 형식적 증명을 지원
- macOS에서 perf 프로파일러가 없어도 dtrace로 충분히 성능 분석이 가능하다는 생각. Perl로 작성된 오리지널 flame graph 스크립트도 dtrace를 활용했고, Rust로 재구현된 flame graph 역시 같은 방식을 사용. cache miss나 micro instruction retired 같은 일부 메트릭은 부족하지만, 여전히 매우 유용
- Rust를 Javascript로 다시 쓸 필요가 있다는 농담투의 의견
- lbzip2와 같이 병렬 디컴프레션(압축 해제)을 지원하는지 궁금. 혹은 block magic 프리 스캔 등으로 병렬 처리가 가능한지 질문. 편집 추가로 ‘아마도 지원하지 않는 듯’이라는 결론
- Lbzip2는 모든 CPU 코어를 활용해 매우 빠른 디컴프레션 속도를 보여줬던 경험 소개. 2025년이 되었지만 Python 같은 많은 주요 프로그램은 여전히 1개 코어만 활용한다는 상황을 아쉬워하는 목소리
- 파이썬의 상황을 잘 이해하지 못한다는 지적