TAKT-Showcase: bzip2-Dekompression (Silesia-Korpus)
Ein Programm in zwei Builds: bz2dec entpackt .bz2-Dateien auf die Standardausgabe, wie bzip2 -dc.
bz2dec-original verwendet das Crate bzip2-rs 0.1.2 (ein
bzip2-Decoder in reinem Rust) in der auf crates.io veröffentlichten Fassung; bz2dec-takt verwendet
dasselbe Crate nach der TAKT-Optimierung. Der Treiber ist dieselbe Quelldatei, das Build-Rezept ist
dasselbe, und die Ausgabe beider Programme ist bis aufs Byte identisch. Das Paket enthält beide
fertigen Programme, den Quellcode des ursprünglichen Builds, ein Skript, das das Korpus herunterlädt,
sowie die Skripte, mit denen sich Messung und Prüfungen wiederholen lassen.
Was der Benchmark macht
Die zwölf Dateien des Silesia-Korpus
(Text, ausführbare Dateien, Datenbanken, Bilder, XML; 211.938.580 Bytes), jeweils mit Pythons
bz2.compress(data, 9) komprimiert (libbz2 1.0.8, ein Stream pro Datei, insgesamt 54.506.769 Bytes),
werden in einem Prozess entpackt:
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
Zum Vergleich führt das System-bzip2 -dc (die C-Referenzimplementierung) dieselbe Aufgabe aus.
Messung (TAKT-Messstand)
AMD Threadripper PRO 5975WX (Zen 3), Linux, ein dedizierter Kern (sein zweiter SMT-Thread bleibt im
Leerlauf, sein CCX ist für die Messung reserviert). perf stat zählt die Takte und Instruktionen des
Dekodierprozesses im User-Modus sowie seine Laufzeit (Wall-Time); 9 abwechselnde Runden (die
Reihenfolge der Programme rotiert von Runde zu Runde), Median. Gemessen wurden genau diese Dateien.
Alle drei Programme sind dynamisch gelinkte glibc-Executables; beide bz2dec-Builds verwenden
dasselbe Rezept: Rust 1.96.0, LTO, codegen-units 1, target-cpu=x86-64-v3, panic abort, gestrippt.
Der gesamte Satz, ein Prozess pro Programm:
| Programm | Decoder | Takte, Mio. | Instruktionen, Mio. | Zeit, ms | MB/s | Beschleunigung |
|---|---|---|---|---|---|---|
bz2dec-original |
bzip2-rs 0.1.2 von crates.io | 17.867,0 | 22.191,1 | 4.129 | 51,3 | 1,00× |
bzip2 -dc |
C-bzip2 / libbz2 1.0.8 (Paket aus Ubuntu 24.04) | 21.148,9 | 25.378,5 | 4.884 | 43,4 | 0,84× |
bz2dec-takt |
dasselbe Crate nach der TAKT-Optimierung | 5.104,7 | 10.028,5 | 1.204 | 176,0 | 3,50× |
bz2dec-takt gegenüber dem C-Programm bzip2 -dc: 4,14× nach Takten, 4,06× nach Zeit.
Gegenüber bz2dec-original nach Zeit: 3,43×. Die Beschleunigung ist das Verhältnis der Mediane der
Takte; MB/s sind entpackte Megabytes (10⁶ Bytes) pro Sekunde Wall-Time.
Jede Datei in einem eigenen Prozess (dieselben Runden; Zeit in ms, Beschleunigung nach Takten):
| Datei | Unkomprimiert, MB | Original, ms | TAKT, ms | bzip2 -dc, ms |
ggü. Original | ggü. 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× |
Die Beschleunigung hängt von den Daten ab: auf diesen Dateien von 2,96× bis 4,32× gegenüber dem
Original und von 3,43× bis 4,87× gegenüber bzip2 -dc. Die Wall-Time umfasst außerdem Prozessstart,
Lesen der Eingabe und Kernel-Arbeit, die für alle Programme gleich sind; deshalb fallen die
Verhältnisse nach Zeit etwas niedriger aus als nach Takten. Andere Daten und andere CPUs ergeben
andere Zahlen. Details und alle Einzelmessungen stehen in measurements.json.
Ausführen
python3 tools/fetch_silesia.py # lädt silesia.zip (68 MB) herunter, prüft es, schreibt data/raw und data/bz2
tools/run_bench.sh 7 2 # prüft jedes Ausgabebyte, dann 7 abwechselnde Runden auf Kern 2
bin/bz2dec-takt -c data/bz2/dickens.bz2 | cmp - data/raw/dickens # bis aufs Byte identisch
sha256sum -c SHA256SUMS
tools/fetch_silesia.py benötigt nur Python 3 und prüft das Archiv (sha256) und jede Datei (Größe,
die auf der Korpus-Seite veröffentlichte MD5, sha256); außerdem meldet es, ob Ihre .bz2-Eingaben
exakt die von uns gemessenen Bytes sind. tools/run_bench.sh zählt auch Takte, wenn perf
verfügbar ist.
Aufruf: bz2dec -c FILE.bz2 [FILE.bz2 ...] > OUT. Exit-Codes: 0 Erfolg; 1 Aufruf-, E/A- oder
CPU-Fehler; 2 ungültiger Stream (die Fehlermeldung des Decoders wird auf stderr ausgegeben);
3 interner Decoder-Fehler.
Linux x86-64 (glibc). Beide Programme sind für x86-64-v3 gebaut: AVX2, BMI1, BMI2, FMA (Intel Haswell und neuer, AMD Zen und neuer); auf älteren CPUs laufen sie nicht.
Äquivalenz
- Die zwölf Silesia-Dateien: Die Ausgabe von
bz2dec-original,bz2dec-taktundbzip2 -dcstimmt Byte für Byte mit der unkomprimierten Datei überein, Datei für Datei und als ein Lauf über alle zwölf Dateien. - 12.900 weitere Streams: 1.200 gültige (zufällige Ausschnitte der Korpusdateien, Kompressionsstufen
1–9) und 11.700 beschädigte (900 beschädigte Kopien der vollständigen Eingaben, der Rest beschädigte
Ausschnitte: Bit-Flips, Abschneiden, Byte-Ersetzung, genullte Bereiche, eingefügte und gelöschte
Bereiche). Für jeden Stream sind stdout, stderr und Exit-Code beider Programme identisch (Exit-Code 0:
1.360 Streams, Exit-Code 2: 11.540); 0 Abweichungen. Wiederholen lässt sich das mit
python3 tools/compare_corrupted.py bin/bz2dec-original bin/bz2dec-takt data --seed 1(verwendet wurden die Seeds 1 und 2, siehemeasurements.json).
Original bauen
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
Bei unserer Prüfung ergab dies bin/bz2dec-original Bit für Bit (Rust 1.96.0 aus rustup, in einem
anderen Verzeichnis). bin/bz2dec-takt ist dieselbe source/src/main.rs, gelinkt mit dem TAKT-Build
des Crates, der die öffentliche API, den Fehlertyp und die Meldungen des Crates beibehält.
Inhalt
bin/:bz2dec-originalundbz2dec-takt;source/: der Treiber und die Cargo-Dateien, aus denenbz2dec-originalgebaut wird;tools/:fetch_silesia.py(Korpus und Eingaben),run_bench.sh(Prüfung und Zeitmessung),compare_corrupted.py(gültige und beschädigte Streams);measurements.json,SHA256SUMS,LICENSE.
Der Quellcode des TAKT-Builds von bzip2-rs wird nicht veröffentlicht: Wir liefern Builds.