Demostración de TAKT: descompresión bzip2 (corpus Silesia)
Un programa en dos compilaciones: bz2dec descomprime archivos .bz2 a la salida estándar, como
bzip2 -dc. bz2dec-original usa el crate bzip2-rs 0.1.2 (un
decodificador bzip2 escrito íntegramente en Rust) tal como está publicado en crates.io;
bz2dec-takt usa el mismo crate tras la optimización de TAKT. El programa de control es el mismo
archivo fuente, la receta de compilación es la misma y la salida de ambos programas es idéntica
byte a byte. Aquí encontrará los dos programas listos, el código fuente de la compilación original,
un script que descarga el corpus y los scripts que repiten la medición y las verificaciones.
Qué hace el benchmark
Los doce archivos del corpus Silesia
(texto, ejecutables, bases de datos, imágenes, XML; 211.938.580 bytes), cada uno comprimido con
bz2.compress(data, 9) de Python (libbz2 1.0.8, un flujo por archivo, 54.506.769 bytes en total),
se descomprimen en un solo proceso:
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
Como comparación, el bzip2 -dc del sistema (la implementación de referencia en C) ejecuta la misma
tarea.
Medición (banco de TAKT)
AMD Threadripper PRO 5975WX (Zen 3), Linux, un núcleo dedicado (su hilo SMT gemelo inactivo y su
CCX reservado para la medición). perf stat cuenta los ciclos y las instrucciones en modo usuario
del proceso de decodificación, así como su tiempo de reloj; 9 rondas intercaladas (el orden de los
programas rota de una ronda a otra), mediana. Se midieron exactamente estos archivos. Los tres
programas son ejecutables glibc con enlace dinámico; las dos compilaciones de bz2dec usan la misma
receta: Rust 1.96.0, LTO, codegen-units 1, target-cpu=x86-64-v3, panic abort, sin símbolos.
El conjunto completo, un proceso por programa:
| Programa | Decodificador | Ciclos, M | Instrucciones, M | Tiempo, ms | MB/s | Aceleración |
|---|---|---|---|---|---|---|
bz2dec-original |
bzip2-rs 0.1.2 de crates.io | 17.867,0 | 22.191,1 | 4.129 | 51,3 | 1,00× |
bzip2 -dc |
bzip2 en C / libbz2 1.0.8 (paquete de Ubuntu 24.04) | 21.148,9 | 25.378,5 | 4.884 | 43,4 | 0,84× |
bz2dec-takt |
el mismo crate tras la optimización de TAKT | 5.104,7 | 10.028,5 | 1.204 | 176,0 | 3,50× |
bz2dec-takt frente al bzip2 -dc en C: 4,14× en ciclos, 4,06× en tiempo.
Frente a bz2dec-original en tiempo: 3,43×. La aceleración es el cociente de las medianas de
ciclos; MB/s son megabytes descomprimidos (10⁶ bytes) por segundo de tiempo de reloj.
Cada archivo en su propio proceso (las mismas rondas; tiempo en ms, aceleración en ciclos):
| Archivo | Sin comprimir, MB | Original, ms | TAKT, ms | bzip2 -dc, ms |
frente al original | frente a 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× |
La aceleración depende de los datos: de 2,96× a 4,32× frente al original y de 3,43× a 4,87× frente
a bzip2 -dc en estos archivos. El tiempo de reloj incluye también el arranque del proceso, la
lectura de la entrada y el trabajo del kernel, que son iguales para todos los programas; por eso los
cocientes en tiempo son algo menores que los cocientes en ciclos. Otros datos y otras CPU darán
otras cifras. Los detalles y todas las muestras están en measurements.json.
Ejecución
python3 tools/fetch_silesia.py # descarga silesia.zip (68 MB), lo verifica y escribe data/raw y data/bz2
tools/run_bench.sh 7 2 # verifica cada byte de salida y luego hace 7 rondas intercaladas en el núcleo 2
bin/bz2dec-takt -c data/bz2/dickens.bz2 | cmp - data/raw/dickens # idénticos byte a byte
sha256sum -c SHA256SUMS
tools/fetch_silesia.py solo necesita Python 3 y verifica el archivo comprimido (sha256) y cada
archivo del corpus (tamaño, el MD5 publicado en la página del corpus, sha256); también le indica si
sus entradas .bz2 son exactamente los bytes que medimos. tools/run_bench.sh también cuenta ciclos
cuando perf está disponible.
Uso: bz2dec -c FILE.bz2 [FILE.bz2 ...] > OUT. Códigos de salida: 0, éxito; 1, error de uso, de E/S
o de CPU; 2, flujo no válido (el mensaje de error del decodificador se imprime en stderr); 3, error
interno del decodificador.
Linux x86-64 (glibc). Ambos programas están compilados para x86-64-v3: AVX2, BMI1, BMI2, FMA (Intel Haswell y posteriores, AMD Zen y posteriores); no funcionan en CPU más antiguas.
Equivalencia
- Los doce archivos de Silesia: la salida de
bz2dec-original,bz2dec-taktybzip2 -dces igual al archivo sin comprimir byte a byte, archivo por archivo y en una sola ejecución con los doce. - 12.900 flujos adicionales: 1.200 válidos (fragmentos aleatorios de los archivos del corpus, niveles
de compresión 1–9) y 11.700 corruptos (900 copias corruptas de las entradas completas; el resto,
fragmentos corruptos: inversión de bits, truncamiento, sustitución de bytes, rangos puestos a cero,
rangos insertados y eliminados). Para cada flujo, la stdout, la stderr y el código de salida de los
dos programas son idénticos (código de salida 0: 1.360 flujos; código de salida 2: 11.540);
0 discrepancias. Puede repetirlo con
python3 tools/compare_corrupted.py bin/bz2dec-original bin/bz2dec-takt data --seed 1(se usaron las semillas 1 y 2; consultemeasurements.json).
Compilar el 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
En nuestra verificación, esto reconstruyó bin/bz2dec-original bit a bit (Rust 1.96.0 de rustup, en
otro directorio). bin/bz2dec-takt es el mismo source/src/main.rs enlazado con la compilación de
TAKT del crate, que conserva la API pública del crate, su tipo de error y sus mensajes.
Contenido
bin/:bz2dec-originalybz2dec-takt;source/: el programa de control y los archivos de Cargo a partir de los cuales se compilabz2dec-original;tools/:fetch_silesia.py(corpus y entradas),run_bench.sh(verificación y cronometraje),compare_corrupted.py(flujos válidos y corruptos);measurements.json,SHA256SUMS,LICENSE.
No se publica el código fuente de la compilación de TAKT de bzip2-rs: entregamos compilaciones.