Cómo Zig logra recompilaciones incrementales en milisegundos
Zig's Incremental Compilation Internals

Como miembro del equipo central de Zig, he trabajado en la implementación de la compilación incremental, una función que detecta cambios específicos y parchea el binario resultante. Esto permite que las aplicaciones complejas se reconstruyan en milisegundos. El sistema divide la compilación en unidades de análisis independientes, optimizando el proceso mediante caché y paralelismo masivo.
Hoy, usando la compilación incremental de Zig, puedes hacer cambios en aplicaciones reales y complejas en cuestión de milisegundos.
- steveklabnik
El trabajo de la herramienta de Zig es constantemente impresionante. Aunque sigo sin planear escribir software en él, dado que creo que la seguridad de memoria es un requisito básico, todo esto es muy, muy bueno. Antes del trabajo incremental, era el trabajo de la herramienta y del compilador cruzado. La parte de la herramienta ha sido constantemente fantástica. ¡Estoy muy curioso para ver qué es lo que sacarán a continuación!
> El análisis semántico es la parte más difícil del compilador para manejar de forma incremental. Quizás no sorprenda entonces que este sea el lugar donde el diseño del lenguaje empieza a importar mucho: aunque estoy bastante seguro de que la mayoría de los lenguajes modernos podrían soportar compilación incremental similar a como lo hacemos, ciertas decisiones de diseño pueden hacer que eso sea mucho más difícil. El diseño de Zig ha sido ajustado a lo largo de los años (a veces de forma controvertida) específicamente para que sea más fácil soportar una compilación incremental rápida.
Esto es algo que desearía que hubiéramos hecho con Rust. Sin embargo, es imposible hacer todas las cosas a la vez, y ya teníamos una cantidad tremenda de cosas que hacer. Esto también es parte de la compensación de "cuándo lanzar la versión 1.0"; para nuestros objetivos con el lenguaje, 2015 fue el momento adecuado para lanzarlo, pero si hubiéramos tenido unos años más para dejar las cosas asentadas, quizás podríamos haber hecho que los tiempos de compilación fueran mucho más rápidos. La ingeniería de software es difícil.
- afdbcreid
Esta publicación es realmente interesante. Como miembro del equipo de rust-analyzer, no puedo evitar compararla con la situación en el mundo de Rust. Es famoso que Rust no tiene un sistema menos (o incluso más) sofisticado para la compilación incremental, y sin embargo, su compilación es mucho más lenta. Atribuyo eso a dos cosas principales:
- Diseño del lenguaje. Zig fue diseñado para una compilación rápida e incremental, Rust simplemente no lo es. Por ejemplo, la publicación afirma que Zig tiene cuatro propiedades (layout, type, value, body) que el compilador debe rastrear para cambios. Rust tiene muchas más, hasta el punto de que rastrearlas estáticamente es simplemente imposible, por lo que el compilador usa un sistema de consultas que las rastrea dinámicamente, lo cual añade sobrecarga.
- Implementación del compilador. Rust es mucho más complicado de compilar que Zig, y rustc es tanto más antiguo como más grande (10x-20x líneas de código) que el compilador de Zig, lo que hace que cambiarlo sea mucho más difícil.
- thefaux
Hay algo de este diseño que no entiendo completamente: ¿por qué insisten en construir un binario gigante para las compilaciones de depuración que contenga todo el código? Desde mi perspectiva, un enfoque más simple sería generar muchas librerías compartidas más pequeñas (quizás a nivel de archivo) y enlazarlas en el binario final. Con este enfoque, el binario del programa tendría una sección de texto diminuta y una (potencialmente larga) lista de librerías compartidas para cargar. Pero incluso con miles de librerías compartidas para cargar, el binario resultante del programa no sería tan largo y no habría necesidad de parchear binarios.
Entiendo que para un modo de lanzamiento un único binario gigante puede ser deseable, pero me cuesta entender este diseño para las compilaciones de depuración. Además, mientras leía este artículo, me preguntaba qué pasaría si el binario principal se corrompe. Quizás el usuario cancele la compilación con ctrl+c mientras está parcheando el binario. Incluso si tienen una estrategia para evitar y/o detectar la corrupción, es más simple no parchear en primer lugar y generar siempre un nuevo binario principal. De nuevo, esto es razonable porque el nuevo binario es principalmente solo una lista de librerías compartidas para enlazar, lo cual no ocupará mucho espacio y se puede escribir en el disco rápidamente. Además, este proceso se puede hacer recursivamente, por ejemplo, a nivel de subdirectorio, de modo que durante el enlazado incremental se puedan producir unas pocas librerías compartidas bastante pequeñas en lugar de parchear el nuevo código y escribir reubicaciones en cascada.
- anitil
Me estoy convirtiendo en un gran fan de Zig desde que aprendí sobre `zig cc` como una forma de meter los pies en el agua. Ya estaba impresionado por la caché de compilación, así que estoy ansioso por probar esto.
- patrec
> Las dependencias del cuerpo de una función de tiempo de ejecución son imposibles (al menos en la vista simplificada que estoy presentando aquí)
¿Cómo funciona esto dado que, por ejemplo, una constante puede ser computada por una función de comptime?