- El interior de zig build ahora se divide en procesos de configurador y creador, y el grafo de compilación generado por
build.zig se serializa como un archivo de configuración binario
- El proceso padre almacena en caché el archivo de configuración y compila al creador en modo Release de forma asíncrona; el creador se compila solo una vez en la caché global por cada versión de Zig
- Cuando el usuario modifica
build.zig, ya no se compila junto con él todo el sistema de compilación, lo que reduce el costo en tiempo de añadir funciones como --watch, --fuzz y --webui
- El tiempo promedio de ejecución de
zig build --help bajó de 150ms a 14.3ms, y los ciclos de CPU, la cantidad de instrucciones y las referencias de caché también se redujeron entre 94% y 96%
- La mayoría de las API siguen siendo compatibles, pero la observación directa de
b.args se reemplaza por addPassthruArgs(), y Zig 0.17.0 está previsto para dentro de unas semanas
Cambio en la arquitectura del sistema de compilación
- Se fusionó una rama grande, separando el interior de
zig build en un proceso configurador y un proceso creador
- En la estructura anterior, el archivo
build.zig y toda la implementación del sistema de compilación se compilaban como un único proceso grande en modo Debug, y build.zig construía el grafo de compilación en memoria antes de que el “build runner” lo ejecutara
- En la nueva estructura,
build.zig se compila como un pequeño proceso configurador en modo Debug, y el grafo de compilación se serializa como un archivo de configuración binario
- El proceso padre
zig build detecta el archivo de configuración, lo guarda en caché para la siguiente ejecución y compila de forma asíncrona en modo Release al creador encargado de ejecutar el grafo de compilación
- Cuando el archivo de configuración y la compilación del creador están listos, el creador recibe el archivo y se ejecuta; gracias a la caché global, el creador solo necesita compilarse una vez por cada
zig version
Efectos en la mejora de velocidad
- El objetivo principal es mejorar la velocidad de
zig build, y al cambiar build.zig del usuario ya no se recompila junto con él todo el sistema de compilación
- Esto cobra más importancia porque, aunque se agreguen más funciones al sistema de compilación con
--watch, --fuzz y --webui, el tiempo de zig build ya no crecerá junto con ellas
- Si se determina que no hubo cambios, se puede reutilizar la configuración anterior sin volver a ejecutar la lógica de
build.zig
- Por ejemplo, aunque se agregue
-freference-trace a la línea de comandos de zig build, no hace falta volver a ejecutar innecesariamente la lógica de build.zig
- Como el proceso que ejecuta el grafo de compilación real se compila con optimizaciones activadas, la etapa de ejecución también es más rápida
Resultados de benchmark
- El tiempo promedio de ejecución de
zig build --help se redujo de 150ms en la estructura anterior a 14.3ms en la nueva
- El tiempo de reloj de pared bajó de
150ms a 14.3ms, una reducción de 90.4%, y los ciclos de CPU pasaron de 593M a 24.1M, una reducción de 95.9%
- La cantidad de instrucciones cayó de
995M a 43.7M, una reducción de 95.6%, y las referencias de caché bajaron de 25.8M a 1.46M, una reducción de 94.3%
- El RSS máximo se redujo de
84.8MB a 78.5MB, una reducción de 7.4%
- La mayor diferencia proviene del cambio desde un enfoque que ejecutaba la lógica de
build.zig en cada comando zig build a otro que reutiliza una configuración serializada almacenada en caché
Impacto en herramientas y compatibilidad
- Las herramientas de terceros como ZLS pueden beneficiarse al consumir el archivo de configuración serializado en lugar de seguir manteniendo una bifurcación del build runner
- Aunque los mecanismos internos cambiaron mucho, desde el punto de vista de la API se mantiene en su mayor parte la compatibilidad
- Las excepciones corresponden a los cambios resumidos en el PR fusionado
Principales cambios incompatibles
- El cambio con más probabilidad de afectar a muchos usuarios es la eliminación del patrón que observaba directamente
b.args
- Código anterior:
if (b.args) |args| {
run_cmd.addArgs(args);
}
run_cmd.addPassthruArgs();
- Con este cambio, los scripts de compilación ya no pueden observar esos argumentos, por lo que se elimina una funcionalidad
- A cambio, cambiar esos argumentos ya no obliga a recompilar el script de compilación desde el código fuente
Pruebas y calendario de lanzamiento
- Los usuarios que quieran influir en la dirección de Zig pueden actualizar sus proyectos a la versión de desarrollo para probar los cambios nuevos y dar retroalimentación
- Zig
0.17.0 está previsto para lanzarse dentro de unas semanas
- Como no hubo tiempo para probarlo de antemano, incluso si alguna compilación se rompe en
0.17.0, todavía habrá suficiente oportunidad para incluir correcciones en la etiqueta 0.17.1
1 comentarios
Comentarios en Hacker News
Probé migrar parte de mi código a Zig 0.16.0 y el resultado me dejó bastante satisfecho
Aunque hubo muchísimas partes afectadas, los cambios en sí fueron buenos y parecen ir en una dirección que hace ver prometedor el futuro del lenguaje
En particular, el nuevo mecanismo de entrada/salida permite código eficiente con una forma elegante tanto en implementaciones de un solo hilo como de varios hilos y bucles de eventos
Si todavía no has usado Zig desde la 0.16.0, vale la pena echarle un vistazo. Las notas de esta versión fueron larguísimas
https://ziglang.org/download/0.16.0/release-notes.html
Hasta donde sé, es más lento que antes
Espero que en las próximas versiones resuelvan el problema de que “el destino del despacho se conoce en tiempo de compilación pero sigue siendo dinámico”, para reducir esa pérdida de eficiencia
Supongo que cuando llamas a
io.asyncen una implementación de IO basada en un bucle de eventos, internamente inicia algo parecido a una “tarea”, y el future sería su handleLa parte que no entiendo es cuando se llama a
future.await(io). ¿La implementación de IO suspende de algún modo la función actual y luego la reanuda cuando el future se resuelve? Si es así, ¿significa eso que todas las funciones de Zig son corutinas sin pila?.use_llvm = trueen los builds de ZigDespués de usar Zig durante unos meses, terminé convencido de que es un excelente lenguaje para herramientas
Es bueno para tomarlo y armar ideas libremente y de forma improvisada, y en cada punto donde uno se traba suele haber una solución cómoda que quienes lo hicieron ya pensaron
Pero eso no significa que te obligue a usar el lenguaje de la manera “correcta”
Ahora se convirtió en mi lenguaje base para “andar cacharreando en el garaje”
Para mí eso sí es un problema importante de productividad
Mi lenguaje base para “andar cacharreando en el garaje” es Python. Sintaxis ligera, uso sin fricciones, biblioteca estándar abundante, y para lo que no hay, existen paquetes para todo
¿Cuál sería la ventaja de Zig?
Vi un video de entrevista a Andrew Kelley y me dieron ganas de aprender Zig: https://www.youtube.com/watch?v=iqddnwKF8HQ
Las respuestas de Andrew estuvieron bien, pero el ambiente general se sintió demasiado adulador
Quería probar Zig, pero el lenguaje todavía cambia demasiado rápido
En cada lanzamiento rompen APIs, así que me resultó difícil seguir al mismo tiempo el aprendizaje del lenguaje, el debugging del sistema de build y la implementación de lo que realmente quería hacer
Después de ver la entrevista de JetBrains sí me dieron ganas de intentarlo otra vez, pero probablemente espere hasta que salga la 1.0
Desde hace mucho vengo pensando en una idea que llamo programación dual
Consiste en construir el stack con exactamente dos lenguajes: uno de alto nivel y otro de bajo nivel
La idea es escribir tanto como sea posible en el lenguaje de alto nivel, y bajar al de bajo nivel solo cuando haga falta
El problema es que, si no conoces ya muy bien el lenguaje de bajo nivel, es muy probable que tengas que volver a familiarizarte con él antes de hacer trabajo de bajo nivel
Por eso C++ o Rust se me hacen más difíciles de retomar que C, y C termina siendo mi opción por defecto. Pero C también tiene problemas bien conocidos
Zig parece poder ocupar bien ese punto justo, porque es lo bastante simple como para retomarlo incluso después de una pausa larga, y al mismo tiempo tiene herramientas modernas que facilitan programar
Intento hacer tanto como sea posible en el lenguaje de bajo nivel, y solo subir al de alto nivel cuando la conveniencia realmente justifica el costo
Roc hace posible esto. Todos los programas tienen una plataforma escrita en un lenguaje de bajo nivel, y los programas de Roc usan la API que esa plataforma expone
https://www.roc-lang.org/
Claro que cómo equilibrar lo de alto y bajo nivel lo puede decidir cada quien
Los modelos se implementan en Cython y la API orientada al usuario se ofrece en Python
Ahora hago todo con Rust y, especialmente gracias a su sistema de tipos al estilo OCaml, todavía no he encontrado nada que no pueda hacer
Lua fue pensado como lenguaje embebido, así que tiene buena interoperabilidad y al mismo tiempo es de alto nivel, por lo que resulta fácil de usar
Hay una razón por la que Factorio usa Lua como lenguaje de scripting
Lo que me gusta del desarrollo de Zig es que, más que añadir funciones al lenguaje, invierten una cantidad sorprendente de esfuerzo en las herramientas y el ciclo de retroalimentación del desarrollador
Un lenguaje nuevo puede sobrevivir un tiempo aunque le falte alguna función
Pero si compilar, enlazar y actualizar dependencias se siente lento cada vez, le resulta mucho más difícil sobrevivir
Ese enfoque en llevar el ciclo de desarrollo no a segundos sino a milisegundos parece una muy buena apuesta a largo plazo
Me sorprende leer que “planean lanzar la 0.17.0 en unas semanas”
¿La 0.16 no tardó más de un año?
No esperaba una 0.17 tan rápida, pero me da mucho gusto enterarme hoy
Suena como una buena noticia. El tiempo de compilación de Zig ya es excelente, y parece que con este cambio será aún mejor.
Sin duda es una meta importante, y los hitos sobre cómo alcanzarla están claros, pero en la práctica cuesta llamar “excelente” a la dolorosa espera cuando se compila por primera vez un proyecto vacío o cuando ZLS vuelve a compilarse después de
direnv allow.zig test file.zig -OReleaseSafe, en mi computadora tarda varios segundos.Y cada vez que modifico el archivo vuelve a tardar lo mismo. No es que esté usando un toolchain viejo, porque uso 0.16 o master, y además estoy en Linux.
El lenguaje Zig en sí es realmente muy agradable de usar, pero me parece que el compilador y la biblioteca estándar no se están desarrollando con suficiente conservadurismo.
Puede que no te topes con estos problemas cuando empiezas con un hello world, pero si quieres hacer fuzz testing o benchmarks, terminas queriendo ejecutar un binario optimizado.
Y entonces compilar incluso una cantidad relativamente pequeña de código se vuelve demasiado frustrante.
En comparación, creo que Zig es mucho mejor como lenguaje que Rust/C++/C, pero este tipo de problemas en realidad casi nunca ocurren en Rust/C++/C. En C/C++ asumiendo que usas clang/gcc/ninja, claro.
En la misma computadora puedo configurar, compilar (-O2 o -O3) y probar un proyecto de C++ de unas 10 mil líneas con Ninja/Python/clang en 200 ms.
Estaría buenísimo que Zig tuviera un mecanismo oficial para exportar stubs de bibliotecas de Linux.
La compilación cruzada de Zig y su capacidad de apuntar a versiones arbitrarias de glibc son magia pura.
Estoy aprovechando esa magia en un sistema de build de C++ separado, pero para obtener esos stubs de bibliotecas desde Zig tengo que dar rodeos.
Sería bueno que se ofrecieran como una salida oficial.
¿Qué motivo habría para querer usar esto en lugar de Node.js y TypeScript?
Si no necesitas exprimir hasta la última gota de rendimiento ni layout y control de memoria, usar Zig tiene más desventajas.
Para trabajo CRUD, “enterprise” o sitios web, Zig casi no ofrece ventajas.
Un programa compilado en Zig puede ocupar apenas unos pocos KB y no tener dependencias.
El acceso a arreglos escrito en un lenguaje de bajo nivel puede optimizarse con SIMD y paralelización, y puede ser varios órdenes de magnitud más rápido que hacer lo mismo en JavaScript.
En procesamiento de texto, manipulación de imágenes, procesamiento de video, hashing y más, la diferencia es grande.
En realidad hay prácticamente infinitas razones para no usar JavaScript.