← 쇼케이스로 돌아가기

TAKT 쇼케이스: bzip2 압축 해제 (Silesia 코퍼스)

하나의 프로그램을 두 가지로 빌드했습니다. bz2dec는 bzip2 -dc처럼 .bz2 파일을 표준 출력으로 압축 해제합니다. bz2dec-original은 crates.io에 공개된 그대로의 bzip2-rs 0.1.2 크레이트(순수 Rust bzip2 디코더)를 사용하고, bz2dec-takt는 TAKT 최적화를 거친 같은 크레이트를 사용합니다. 드라이버는 같은 소스 파일이고 빌드 레시피도 같으며, 두 프로그램의 출력은 바이트 단위로 동일합니다. 이 패키지에는 실행 가능한 두 프로그램, 원본 빌드의 소스, 코퍼스를 내려받는 스크립트, 측정과 검증을 재현하는 스크립트가 들어 있습니다.

벤치마크 내용

Silesia 코퍼스의 파일 12개(텍스트, 실행 파일, 데이터베이스, 이미지, XML; 211,938,580바이트)를 각각 Python의 bz2.compress(data, 9)(libbz2 1.0.8, 파일당 스트림 1개, 합계 54,506,769바이트)로 압축한 뒤, 하나의 프로세스에서 압축 해제합니다:

bz2dec -c dickens.bz2 mozilla.bz2 mr.bz2 nci.bz2 ooffice.bz2 osdb.bz2 reymont.bz2 \
          samba.bz2 sao.bz2 webster.bz2 xml.bz2 x-ray.bz2 > /dev/null

비교를 위해 시스템의 bzip2 -dc(C 참조 구현)로 같은 작업을 실행합니다.

측정 (TAKT 측정 장비)

AMD Threadripper PRO 5975WX (Zen 3), Linux, 전용 코어 1개(SMT 형제 스레드는 유휴 상태, 해당 CCX는 측정 전용으로 예약). perf stat으로 디코딩 프로세스의 사용자 모드 사이클과 명령어 수, 실제 경과 시간을 측정합니다. 교차 실행 9라운드(라운드마다 프로그램 실행 순서를 순환), 중앙값입니다. 이 패키지의 파일을 그대로 측정했습니다. 세 프로그램 모두 glibc에 동적 링크된 실행 파일이며, 두 bz2dec 빌드는 같은 레시피를 사용합니다: Rust 1.96.0, LTO, codegen-units 1, target-cpu=x86-64-v3, panic abort, 심볼 제거(stripped).

전체 세트, 프로그램당 프로세스 1개:

프로그램 디코더 사이클(백만) 명령어(백만) 시간(ms) MB/s 가속
bz2dec-original crates.io의 bzip2-rs 0.1.2 17,867.0 22,191.1 4,129 51.3 1.00×
bzip2 -dc C bzip2 / libbz2 1.0.8 (Ubuntu 24.04 패키지) 21,148.9 25,378.5 4,884 43.4 0.84×
bz2dec-takt TAKT 최적화를 거친 같은 크레이트 5,104.7 10,028.5 1,204 176.0 3.50×

bz2dec-takt는 C bzip2 -dc 대비 사이클 기준 4.14배, 시간 기준 4.06배 빠릅니다. bz2dec-original 대비 시간 기준으로는 3.43배입니다. 가속은 사이클 중앙값의 비율이며, MB/s는 실제 경과 시간 1초당 압축 해제된 메가바이트(10⁶바이트)입니다.

파일마다 별도 프로세스로 실행(같은 라운드, 시간은 ms, 가속은 사이클 기준):

파일 압축 전(MB) 원본(ms) TAKT(ms) bzip2 -dc(ms) 원본 대비 bzip2 -dc 대비
dickens 10.2 247.3 65.8 286.7 4.16× 4.82×
mozilla 51.2 1048.6 370.9 1272.9 2.96× 3.64×
mr 10.0 177.2 48.4 206.1 4.04× 4.64×
nci 33.6 461.7 111.0 516.6 4.32× 4.81×
ooffice 6.2 169.4 59.0 205.5 3.20× 3.95×
osdb 10.1 229.3 63.0 265.5 3.92× 4.68×
reymont 6.6 136.0 35.8 152.9 4.25× 4.87×
samba 21.6 333.1 115.0 404.9 3.08× 3.75×
sao 7.3 241.3 85.7 270.0 3.05× 3.43×
webster 41.5 843.6 216.1 972.2 4.10× 4.79×
xml 5.3 72.3 20.9 82.6 4.04× 4.64×
x-ray 8.5 236.4 80.1 255.6 3.22× 3.50×

가속은 데이터에 따라 다릅니다. 이 파일들에서는 원본 대비 2.96–4.32배, bzip2 -dc 대비 3.43–4.87배입니다. 실제 경과 시간에는 프로세스 시작, 입력 읽기, 커널 작업도 포함되며 이 부분은 모든 프로그램에서 같습니다. 그래서 시간 기준 비율이 사이클 기준 비율보다 약간 낮습니다. 다른 데이터와 다른 CPU에서는 수치가 달라집니다. 자세한 내용과 모든 샘플은 measurements.json에 있습니다.

실행

python3 tools/fetch_silesia.py      # silesia.zip(68 MB)을 내려받아 검증하고 data/raw, data/bz2를 생성
tools/run_bench.sh 7 2              # 모든 출력 바이트를 검사한 뒤 코어 2에서 교차 실행 7라운드
bin/bz2dec-takt -c data/bz2/dickens.bz2 | cmp - data/raw/dickens      # 바이트 단위로 동일
sha256sum -c SHA256SUMS

tools/fetch_silesia.py는 Python 3만 있으면 되며, 아카이브(sha256)와 모든 파일(크기, 코퍼스 페이지에 공개된 MD5, sha256)을 검사합니다. 또한 사용자의 .bz2 입력이 저희가 측정한 것과 정확히 같은 바이트인지도 알려 줍니다. tools/run_bench.sh는 perf를 사용할 수 있으면 사이클도 측정합니다.

사용법: bz2dec -c FILE.bz2 [FILE.bz2 ...] > OUT. 종료 코드: 0 성공, 1 사용법·I/O·CPU 오류, 2 잘못된 스트림(디코더의 오류 메시지가 stderr에 출력됨), 3 디코더 내부 오류.

Linux x86-64 (glibc). 두 프로그램 모두 x86-64-v3용으로 빌드했습니다: AVX2, BMI1, BMI2, FMA(Intel Haswell 이후, AMD Zen 이후). 이전 CPU에서는 실행되지 않습니다.

동등성

원본 빌드

cd source
RUSTFLAGS="-C target-cpu=x86-64-v3 --remap-path-prefix=$(ls -d ~/.cargo/registry/src/index.crates.io-*)=. \
  --remap-path-prefix=$HOME/.rustup=. --remap-path-prefix=$PWD=." cargo +1.96.0 build --release --locked
strip --strip-all target/release/bz2dec

저희 검증에서는 이 방법으로 bin/bz2dec-original을 비트 단위로 동일하게 다시 빌드했습니다(rustup의 Rust 1.96.0, 다른 디렉터리에서). bin/bz2dec-takt는 같은 source/src/main.rs를 TAKT 빌드 크레이트와 링크한 것으로, 크레이트의 공개 API, 오류 타입, 메시지를 그대로 유지합니다.

구성

bzip2-rs의 TAKT 빌드 소스는 공개하지 않습니다. 저희는 빌드를 제공합니다.

원문: README.ko.md

Telegram