Vitrine TAKT : décompression bzip2 (corpus Silesia)
Un même programme en deux builds : bz2dec décompresse des fichiers .bz2 vers la sortie standard,
comme bzip2 -dc. bz2dec-original utilise la crate bzip2-rs
0.1.2 (un décodeur bzip2 en Rust pur) telle que publiée sur crates.io ; bz2dec-takt utilise la même
crate après optimisation par TAKT. Le programme pilote est le même fichier source, les options de
compilation sont les mêmes, et la sortie des deux programmes est identique à l’octet près. Vous
trouverez ici les deux programmes prêts à l’emploi, le code source du build d’origine, un script qui
télécharge le corpus et les scripts qui reproduisent la mesure et les vérifications.
Ce que fait le benchmark
Les douze fichiers du corpus Silesia
(texte, exécutables, bases de données, images, XML ; 211 938 580 octets), chacun compressé avec
bz2.compress(data, 9) de Python (libbz2 1.0.8, un flux par fichier, 54 506 769 octets au total),
sont décompressés dans un seul processus :
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
Le bzip2 -dc du système (l’implémentation C de référence) exécute la même tâche à titre de
comparaison.
Mesure (banc TAKT)
AMD Threadripper PRO 5975WX (Zen 3), Linux, un cœur dédié (son jumeau SMT inactif, son CCX réservé à
la mesure). perf stat compte les cycles et les instructions en mode utilisateur du processus de
décodage, ainsi que son temps écoulé ; 9 tours entrelacés (l’ordre des programmes change d’un tour à
l’autre), médiane. Ce sont exactement ces fichiers qui ont été mesurés. Les trois programmes sont des
exécutables glibc liés dynamiquement ; les deux builds de bz2dec utilisent les mêmes options :
Rust 1.96.0, LTO, codegen-units 1, target-cpu=x86-64-v3, panic abort, symboles supprimés.
L’ensemble complet, un processus par programme :
| Programme | Décodeur | Cycles, M | Instructions, M | Temps, ms | Mo/s | Accélération |
|---|---|---|---|---|---|---|
bz2dec-original |
bzip2-rs 0.1.2 depuis crates.io | 17 867,0 | 22 191,1 | 4 129 | 51,3 | 1,00× |
bzip2 -dc |
bzip2 en C / libbz2 1.0.8 (paquet Ubuntu 24.04) | 21 148,9 | 25 378,5 | 4 884 | 43,4 | 0,84× |
bz2dec-takt |
la même crate après optimisation par TAKT | 5 104,7 | 10 028,5 | 1 204 | 176,0 | 3,50× |
bz2dec-takt face au bzip2 -dc en C : 4,14× en cycles, 4,06× en temps.
Face à bz2dec-original en temps : 3,43×. L’accélération est le rapport des médianes de cycles ;
Mo/s correspond aux mégaoctets décompressés (10⁶ octets) par seconde de temps écoulé.
Chaque fichier dans son propre processus (mêmes tours ; temps en ms, accélération en cycles) :
| Fichier | Brut, Mo | Original, ms | TAKT, ms | bzip2 -dc, ms |
face à l’original | face à bzip2 -dc |
|---|---|---|---|---|---|---|
| dickens | 10,2 | 247,3 | 65,8 | 286,7 | 4,16× | 4,82× |
| mozilla | 51,2 | 1 048,6 | 370,9 | 1 272,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× |
L’accélération dépend des données : de 2,96× à 4,32× face à l’original et de 3,43× à 4,87× face à
bzip2 -dc sur ces fichiers. Le temps écoulé inclut aussi le démarrage du processus, la lecture de
l’entrée et le travail du noyau, identiques pour tous les programmes ; c’est pourquoi les rapports en
temps sont un peu inférieurs aux rapports en cycles. D’autres données et d’autres CPU donneront
d’autres chiffres. Les détails et tous les échantillons figurent dans measurements.json.
Exécution
python3 tools/fetch_silesia.py # télécharge silesia.zip (68 Mo), le vérifie, écrit data/raw et data/bz2
tools/run_bench.sh 7 2 # vérifie chaque octet de sortie, puis 7 tours entrelacés sur le cœur 2
bin/bz2dec-takt -c data/bz2/dickens.bz2 | cmp - data/raw/dickens # identique à l’octet près
sha256sum -c SHA256SUMS
tools/fetch_silesia.py ne nécessite que Python 3 et vérifie l’archive (sha256) et chaque fichier
(taille, MD5 publié sur la page du corpus, sha256) ; il indique aussi si vos entrées .bz2 sont
exactement les octets que nous avons mesurés. tools/run_bench.sh compte aussi les cycles lorsque
perf est disponible.
Utilisation : bz2dec -c FILE.bz2 [FILE.bz2 ...] > OUT. Codes de sortie : 0 succès ; 1 erreur
d’utilisation, d’E/S ou de CPU ; 2 flux invalide (le message d’erreur du décodeur est écrit sur
stderr) ; 3 erreur interne du décodeur.
Linux x86-64 (glibc). Les deux programmes sont compilés pour x86-64-v3 : AVX2, BMI1, BMI2, FMA (Intel Haswell et plus récents, AMD Zen et plus récents) ; ils ne fonctionnent pas sur des CPU plus anciens.
Équivalence
- Les douze fichiers Silesia : la sortie de
bz2dec-original,bz2dec-taktetbzip2 -dcest identique au fichier brut octet par octet, fichier par fichier comme en une seule exécution sur les douze fichiers. - 12 900 flux supplémentaires : 1 200 valides (tranches aléatoires des fichiers du corpus, niveaux
de compression 1–9) et 11 700 corrompus (900 copies corrompues des entrées complètes, le reste des
tranches corrompues : inversions de bits, troncature, substitution d’octets, plages mises à zéro,
plages insérées et supprimées). Pour chaque flux, stdout, stderr et le code de sortie des deux
programmes sont identiques (code de sortie 0 : 1 360 flux, code de sortie 2 : 11 540) ;
0 divergence. Pour le reproduire :
python3 tools/compare_corrupted.py bin/bz2dec-original bin/bz2dec-takt data --seed 1(les graines 1 et 2 ont été utilisées, voirmeasurements.json).
Recompiler l’original
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
Lors de notre vérification, cela a reproduit bin/bz2dec-original bit à bit (Rust 1.96.0 installé
via rustup, dans un autre répertoire). bin/bz2dec-takt est le même source/src/main.rs lié au
build TAKT de la crate, qui conserve l’API publique, le type d’erreur et les messages de la crate.
Contenu
bin/:bz2dec-originaletbz2dec-takt;source/: le programme pilote et les fichiers Cargo à partir desquelsbz2dec-originalest compilé ;tools/:fetch_silesia.py(corpus et entrées),run_bench.sh(vérification et chronométrage),compare_corrupted.py(flux valides et corrompus) ;measurements.json,SHA256SUMS,LICENSE.
Le code source du build TAKT de bzip2-rs n’est pas publié : nous livrons des builds.