3 puntos por GN⁺ 2025-06-19 | 1 comentarios | Compartir por WhatsApp
  • El crate de bzip2 reemplazó su dependencia de código C con una implementación 100% en Rust
  • El rendimiento mejora en general frente a antes, y la compilación cruzada se vuelve más sencilla
  • La implementación en Rust mejora tanto la velocidad de compresión como la de descompresión frente a la versión en C
  • Los problemas de dependencias de bibliotecas, como los conflictos de símbolos, se reducen considerablemente
  • Tras una auditoría de seguridad, se corrigieron errores lógicos importantes, validando su estabilidad

Lanzamiento de bzip2 crate 0.6.0 y transición a Rust

  • Hoy se publicó bzip2 versión 0.6.0
  • Ahora usa por defecto libbz2-rs-sys, una implementación del algoritmo bzip2 basada en Rust y desarrollada internamente
  • Con este cambio, el crate bzip2 es más rápido y la compilación cruzada se volvió más fácil
  • El crate libbz2-rs-sys también puede compilarse como una biblioteca dinámica en C. Gracias a esto, los proyectos en C también pueden aprovechar las mejoras de rendimiento

¿Por qué se hizo este cambio?

  • El algoritmo bzip2 fue creado en los años 90 y, aunque hoy ya no se usa tanto, sigue siendo necesario en varios protocolos y bibliotecas para cumplir con la especificación
  • Muchos proyectos dependen de bzip2, no de forma directa, sino en alguna parte profunda de su árbol de dependencias
  • Con base en la experiencia acumulada en zlib-rs, esta vez modernizamos la implementación de bzip2
  • Los detalles de implementación de libbz2-rs-sys se trataron en una entrada anterior del blog. Aquí revisamos las ventajas de esta transición

Mejor rendimiento

  • La implementación en Rust muestra en general un rendimiento superior al de la versión en C
  • En algunos casos el rendimiento es equivalente, pero no hay casos en que sea más lenta
  • Rendimiento de compresión: en bzip2 existe la opción level, pero su impacto en el rendimiento es mínimo
  • Según las pruebas, en archivos de muestra representativos la versión en Rust logró mejoras de velocidad de más del 10%

Compresión:

Archivo C (ciclos de ejecución) Rust (ciclos de ejecución) Cambio relativo
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%

En descompresión también mostró mejores resultados en todos los casos:

Archivo C (ciclos de ejecución) Rust (ciclos de ejecución) Cambio relativo
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%

Sin embargo, en entornos macOS a veces se observan variaciones en las cifras de descompresión. Fue difícil analizarlas por las limitaciones de las herramientas de medición de rendimiento

Soporte para compilación cruzada

  • La compilación cruzada de proyectos Rust con dependencias en C suele funcionar bien gracias al crate cc, pero cuando falla es muy difícil de depurar
  • En el proceso de enlazado con bibliotecas del sistema es fácil que aparezcan problemas inesperados, y en algunos entornos, incluyendo compilaciones para WebAssembly, esto se convierte en un obstáculo real
  • Al cambiar a una implementación en Rust, los problemas relacionados con C desaparecen por completo
  • Ahora es posible compilar de forma cruzada para Windows, Android, WebAssembly y otros entornos sin casos especiales
  • Esto es una gran ventaja no solo para la experiencia de usuario, sino también desde la perspectiva de mantenimiento

Sin conflictos de símbolos (export) por defecto

  • En las dependencias en C, es necesario exportar símbolos desde bloques externos a Rust, por lo que puede haber conflictos si otra dependencia exporta los mismos símbolos
  • libbz2-rs-sys está diseñado para no exportar símbolos por defecto
  • Por eso no deberían surgir conflictos de símbolos con otras bibliotecas externas. Si hace falta, la exportación puede activarse con un feature flag

Ejecución de pruebas basadas en MIRI

  • Para implementar bzip2 con alto rendimiento en Rust, el uso de código unsafe es inevitable, y también se necesita bastante unsafe para replicar la interfaz en C
  • Afortunadamente, este código puede ejecutarse y probarse en un entorno MIRI
  • Más aún, las bibliotecas o aplicaciones de nivel superior que usan bzip2 ahora también pueden ejecutar pruebas con MIRI

Conclusión

Ahora el crate bzip2 es más rápido. Ofrece de forma natural una mejor experiencia, hasta el punto de que ya no hace falta preocuparse por ello

1 comentarios

 
GN⁺ 2025-06-19
Comentarios en Hacker News
  • Considerando la posibilidad de que la implementación de Trifecta Tech llegue a reemplazar la implementación oficial usada por las distribuciones de Linux, no parece imposible, dado que Fedora antes cambió el zlib Adler tradicional por zlib-ng. La opinión es que la clave sería ofrecer un C ABI compatible con el original
    • Si no ha habido lanzamientos upstream desde 2019, surge la duda de si esta implementación simplemente ya está terminada. Si ya no hay bugs por corregir ni funciones por agregar, eso por sí solo podría ser suficiente
    • Como Ubuntu usa un sudo escrito en Rust, parece totalmente posible que ocurra un reemplazo así
    • Aunque Trifecta Tech está ofreciendo bien un C ABI compatible, al final alguien tiene que hacer realmente ese trabajo para que ocurra el cambio
    • Se menciona que el objetivo de uutils también apunta a este tipo de reemplazos oficiales, y se comparte el enlace al sitio de uutils
    • Tras una revisión rápida, ya existe una configuración de cargo-c, lo cual se ve positivo, pero como el namespace es distinto, un programa en C no lo detectaría automáticamente como el libbz2 existente. Se comenta que no es fácil saber si la compatibilidad ABI es exacta porque no se conocen bien los símbolos de bzip2. Mantener directamente una implementación del sistema operativo GNU consume mucho tiempo, así que se agradecen PR para el proyecto experimental platypos cuando haya tiempo
  • Estoy usando este crate para procesar cientos de TB de datos de Common Crawl. Estoy muy satisfecho porque aumentó la velocidad
    • Se pregunta por qué se usa bz2 aquí. Se comenta que, si solo se va a hacer una conversión masiva una sola vez, se ha oído que migrar a zstd tiene muchas ventajas, y que cuanto mejor sea la compresión, en prácticamente todo supera a bzip2
    • Se pregunta si los datos de Common Crawl están publicados en formato torrent
    • Una mejora del 14% en velocidad de compresión parece bastante buena
  • Se pregunta si esta implementación resuelve por defecto los 11 CVE pendientes. Irónicamente, también se reportaron CVE para el crate de bzip2, y se comparte este enlace relacionado
    • Resulta interesante el contraste entre las vulnerabilidades reportadas en este crate, del tipo “fallo en tiempo de ejecución con archivos grandes”, y los problemas de “bounds miss” del caso escrito en C. Se preguntan si una vulnerabilidad de este tipo realmente podría llegar hasta ejecución de código
    • Se cita el aviso de que “el crate bzip2 es vulnerable en versiones anteriores a 0.4.4”. También se agrega que hoy salió la versión 0.6.0
  • Ante la pregunta de “por qué mejorar a la fuerza un algoritmo de los 90”, alguien comenta que le da curiosidad saber qué algoritmos se usan hoy en día. Menciona zstd y comparte este enlace a benchmarks comparativos de algoritmos de compresión
  • Si el backend de generación de código de los compiladores de C y Rust es el mismo, surge la duda de cómo aparece la mejora de velocidad. Se plantean varios factores posibles, como auto-simd de Rust, optimizaciones manuales o el uso de nuevas bibliotecas de optimización
    • Como hipótesis, Rust puede dar más pistas al generador de código. Por ejemplo, a diferencia de los punteros de C, hay menos preocupación por problemas de aliasing. Se comparte este enlace explicativo sobre aliasing
    • Se opina que el lenguaje C realmente no es bueno para escribir código moderno de alto rendimiento. Se critica que, desde C99 hasta C21, durante casi 20 años el lenguaje no ha incorporado una forma limpia de aprovechar nuevas instrucciones como clz, popcnt, clmul, pdep, etc. Se valora que el soporte para este tipo de abstracciones ayuda mucho a optimizar este tipo de código
    • Reescribir en cualquier lenguaje puede abrir oportunidades de mejora de velocidad, y no sería una garantía exclusiva de Rust
  • Se expresa el deseo de que Nana o Prossimo reescriban de forma similar protocolos centrales de Internet (BGP, OSPF, RIP, etc.), implementaciones de routing y servidores DNS
    • Se presentan fondos que en los últimos años han apoyado la reescritura de herramientas clave de Internet y del sistema operativo en Rust y otros lenguajes seguros, con enlaces al proyecto NLnet y al Sovereign Tech Fund. También se menciona como ejemplo el proyecto BGP in Rust
    • En la Memory Safety Initiative se muestran esfuerzos de reescritura segura de servicios clave como TLS y DNS, en un contexto que coincide en parte con esta propuesta
    • Se menciona que un desarrollador creó Ironsides DNS en SPARK Ada, un lenguaje que permite pruebas formales más sólidas
  • En macOS, incluso sin el profiler perf, parece que dtrace basta para analizar rendimiento. El script original de flame graph escrito en Perl también usaba dtrace, y la reimplementación en Rust de flame graph usa el mismo enfoque. Faltan algunas métricas como cache miss o micro instruction retired, pero sigue siendo muy útil
  • Un comentario en tono de broma dice que ahora hace falta reescribir Rust en Javascript
  • Se pregunta si soporta descompresión paralela como lbzip2, o si permite procesamiento paralelo mediante un pre-scan del block magic, por ejemplo. Como edición posterior, se concluye “probablemente no”
  • Se comparte la experiencia de que Lbzip2 mostraba una velocidad de descompresión muy alta al usar todos los núcleos del CPU. Aunque ya estamos en 2025, lamentan que muchos programas importantes, como Python, sigan usando solo 1 núcleo
    • Se señala que no se entiende bien la situación de Python