- O interior de zig build foi dividido em processos de configurador e maker, e o grafo de build criado por
build.zig passa a ser serializado em um arquivo binário de configuração
- O processo pai armazena o arquivo de configuração em cache e compila o maker de forma assíncrona em modo Release; o maker é compilado apenas uma vez no cache global para cada versão do Zig
- Ao alterar o
build.zig do usuário, não é mais necessário compilar junto todo o sistema de build, reduzindo o custo de tempo para adicionar recursos como --watch, --fuzz e --webui
- O tempo médio de execução de
zig build --help caiu de 150ms para 14.3ms, e os ciclos de CPU, número de instruções e referências de cache também caíram entre 94% e 96%
- A maior parte da API continua compatível, mas a observação direta de
b.args é substituída por addPassthruArgs(), e o Zig 0.17.0 está previsto para as próximas semanas
Mudanças na arquitetura do sistema de build
- Um grande branch foi mesclado, separando o interior de
zig build em um processo configurador e um processo maker
- Na estrutura anterior, o arquivo
build.zig e toda a implementação do sistema de build eram compilados juntos como um grande processo em modo Debug, e o build.zig criava na memória o grafo de build, que depois era executado pelo “build runner”
- Na nova estrutura, o
build.zig é compilado como um pequeno processo configurador em modo Debug, e o grafo de build é serializado em um arquivo binário de configuração
- O processo pai
zig build detecta esse arquivo de configuração, o armazena em cache para execuções futuras e compila de forma assíncrona, em modo Release, o maker responsável por executar o grafo de build
- Quando o arquivo de configuração e a compilação do maker ficam prontos, o maker recebe o arquivo e é executado; graças ao cache global, o maker precisa ser compilado apenas uma vez por
zig version
Ganhos de desempenho
- O objetivo principal é melhorar a velocidade do
zig build, já que ao alterar o build.zig do usuário não é mais necessário recompilar junto todo o sistema de build
- Isso ganha ainda mais importância para que o tempo do
zig build não cresça junto com o aumento de recursos do sistema de build, como --watch, --fuzz e --webui
- Quando o sistema conclui que não houve mudanças, ele pode reutilizar a configuração anterior sem executar novamente a lógica de
build.zig
- Por exemplo, mesmo que se adicione
-freference-trace à linha de comando de zig build, a lógica de build.zig não é reexecutada sem necessidade
- Como o processo que executa o grafo de build real é compilado com otimizações ativadas, a etapa de execução também fica mais rápida
Resultados de benchmark
- O tempo médio de execução de
zig build --help caiu de 150ms na estrutura anterior para 14.3ms na nova
- O tempo de relógio caiu de
150ms para 14.3ms, uma redução de 90.4%, e os ciclos de CPU caíram de 593M para 24.1M, uma redução de 95.9%
- O número de instruções caiu de
995M para 43.7M, uma redução de 95.6%, e as referências de cache caíram de 25.8M para 1.46M, uma redução de 94.3%
- O pico de RSS caiu de
84.8MB para 78.5MB, uma redução de 7.4%
- A maior diferença vem da mudança de um modelo em que a lógica de
build.zig era executada em todo comando zig build para um modelo que reutiliza a configuração serializada armazenada em cache
Impacto em ferramentas e compatibilidade
- Ferramentas de terceiros como o ZLS podem se beneficiar ao consumir diretamente o arquivo de configuração serializado, em vez de manter um fork do build runner
- Embora os mecanismos internos tenham mudado bastante, do ponto de vista da API a maior parte da compatibilidade foi mantida
- As exceções correspondem às mudanças listadas no PR mesclado
Principais mudanças incompatíveis
- A alteração que provavelmente mais usuários vão encontrar é a remoção do padrão de observar diretamente
b.args
- Código anterior:
if (b.args) |args| {
run_cmd.addArgs(args);
}
run_cmd.addPassthruArgs();
- Com essa mudança, os scripts de build deixam de poder observar esses argumentos, o que significa que uma funcionalidade foi removida
- Em compensação, mudar esses argumentos deixa de exigir que o script de build seja recompilado a partir do código-fonte
Testes e cronograma de lançamento
- Usuários que queiram influenciar a direção do Zig podem atualizar seus projetos para a versão de desenvolvimento para testar as novas mudanças e enviar feedback
- O Zig
0.17.0 está previsto para ser lançado nas próximas semanas
- Como não houve tempo para testar tudo antecipadamente, mesmo que um build quebre no
0.17.0, ainda haverá tempo suficiente para incluir correções na tag 0.17.1
1 comentários
Comentários do Hacker News
Testei migrar parte do meu código para o Zig 0.16.0 e fiquei bem satisfeito com o resultado
As partes afetadas foram muitas, mas a mudança em si foi boa e parece apontar para um futuro promissor da linguagem
Em especial, o novo mecanismo de entrada/saída permite código eficiente com uma forma elegante tanto em implementações single-thread quanto multithread e também em event loops
Se você ainda não usou Zig depois do 0.16.0, recomendo muito dar uma olhada. As notas desta release ficaram enormes
https://ziglang.org/download/0.16.0/release-notes.html
Pelo que sei, está mais lento do que antes
Espero que nas próximas releases resolvam o problema de “o alvo do despacho é conhecido em tempo de compilação, mas ainda assim continua dinâmico”, para reduzir essa perda de eficiência
Suponho que, ao chamar
io.asyncnuma implementação de IO baseada em event loop, ele internamente inicia algo como uma “task”, e o future seria o handle delaA parte que não entendo é quando se chama
future.await(io). A implementação de IO suspende a função atual de algum jeito e a retoma quando o future é resolvido? Se for isso, significa que todas as funções em Zig são corrotinas sem pilha?.use_llvm = trueno build de ZigDepois de usar Zig por alguns meses, fiquei convencido de que é uma excelente linguagem para ferramentas
É ótima para pegar e montar ideias de forma livre e meio improvisada, e sempre que você emperra em algum ponto, os criadores já pensaram numa solução confortável
Isso sem impor uma forma “correta” de usar a linguagem
Para mim, ela virou a linguagem padrão para “mexer na garagem”
Para mim isso é um problema de produtividade bem grande
Minha linguagem padrão para “mexer na garagem” é Python. Sintaxe leve, uso que não atrapalha, biblioteca padrão rica, e o que falta geralmente existe em pacote
Qual é a vantagem do Zig?
Depois de ver um vídeo de entrevista com Andrew Kelley, fiquei com vontade de aprender Zig: https://www.youtube.com/watch?v=iqddnwKF8HQ
As respostas do Andrew foram boas, mas o clima geral pareceu bajulador demais
Eu queria usar Zig, mas a linguagem ainda está mudando rápido demais
Como ela quebra APIs a cada release, ficou difícil acompanhar ao mesmo tempo o aprendizado da linguagem, o debug do sistema de build e a implementação do que eu realmente queria fazer
Mesmo depois de ver a entrevista da JetBrains, deu vontade de tentar de novo, mas provavelmente vou esperar sair a 1.0
Há muito tempo penso numa ideia que chamo de programação em duas camadas
É um modelo em que a pilha é composta por exatamente duas linguagens: uma de alto nível e uma de baixo nível
Você escreve o máximo possível na linguagem de alto nível e só desce para a de baixo nível quando necessário
O problema é que, a menos que você já conheça muito bem a linguagem de baixo nível, provavelmente vai precisar se reacostumar com ela antes de fazer esse trabalho
Por isso C++ ou Rust acabam sendo mais difíceis de usar do que C, e C vira minha escolha padrão. Mas C tem problemas bem conhecidos
O Zig parece simples o bastante para ser retomado com facilidade mesmo depois de longos intervalos, ao mesmo tempo em que traz ferramentas modernas que facilitam programar, então talvez ocupe bem esse ponto ideal
Fazer o máximo possível na linguagem de baixo nível e só subir para a de alto nível quando a conveniência realmente justificar o custo
O Roc permite isso. Todo programa tem uma plataforma escrita numa linguagem de baixo nível, e o programa em Roc usa a API exposta por essa plataforma
https://www.roc-lang.org/
Claro que como equilibrar alto e baixo nível fica a critério de cada um
Os modelos são implementados em Cython e a API voltada ao usuário é oferecida em Python
Hoje faço tudo em Rust e, especialmente graças a um sistema de tipos no estilo de OCaml, ainda não encontrei algo que eu não conseguisse fazer
Lua foi pensada como linguagem embarcada, então é boa para interoperabilidade e, ao mesmo tempo, é de alto nível e fácil de usar
Há um motivo para Factorio usar Lua como linguagem de script
O que me agrada no desenvolvimento do Zig é que eles dedicam uma quantidade impressionante de esforço a ferramentas e ao ciclo de feedback do desenvolvedor, em vez de só adicionar recursos à linguagem
Uma linguagem nova pode sobreviver por um bom tempo sem um recurso ou outro
Mas, se compilar, linkar e atualizar dependências parecer lento toda vez, fica muito mais difícil sobreviver
Esse foco em transformar o ciclo de desenvolvimento de segundos em milissegundos parece uma boa aposta no longo prazo
Fiquei surpreso com a frase “planejamos lançar o 0.17.0 em algumas semanas”
O 0.16 não levou mais de um ano?
Eu não esperava uma release 0.17 tão rápida, então foi uma ótima surpresa descobrir isso hoje
Parece uma boa notícia. O tempo de compilação do Zig já é excelente, e essa mudança provavelmente vai melhorá-lo ainda mais
É claramente uma meta importante, e os marcos para chegar lá também são claros, mas na prática é difícil chamar de “excelente” a espera dolorosa na primeira compilação de um projeto vazio ou quando o ZLS recompila depois de um
direnv allowzig test file.zig -OReleaseSafe, no meu computador isso leva alguns segundosE o mesmo tempo continua sendo necessário toda vez que o arquivo é modificado. Não é como se eu estivesse usando uma toolchain antiga, já que estou em 0.16 ou no master, e em ambiente Linux
A linguagem Zig em si é realmente ótima de usar, mas acho que o compilador e a biblioteca padrão não estão sendo desenvolvidos de forma suficientemente conservadora
Você pode não encontrar esse tipo de problema começando com hello world, mas, se quiser fazer fuzz testing ou benchmark, vai querer rodar um binário otimizado
Aí compilar até uma quantidade relativamente pequena de código fica irritante demais
Comparando, acho que o Zig é muito melhor como linguagem do que Rust/C++/C, mas esse tipo de problema praticamente quase não acontece em Rust/C++/C. No caso de C/C++, estou assumindo clang/gcc/ninja etc.
No mesmo computador, consigo configurar, compilar (-O2 ou -O3) e testar um projeto C++ de cerca de 10 mil linhas com Ninja/Python/clang em 200ms
Seria muito bom se o Zig tivesse um mecanismo oficial para exportar stubs de bibliotecas Linux
A capacidade de cross-compilation do Zig e de mirar versões arbitrárias do glibc é pura mágica
Estou aproveitando essa mágica em um sistema de build C++ separado, mas preciso contornar isso para obter esses stubs de biblioteca a partir do Zig
Seria bom se isso viesse como saída oficial
Qual seria o motivo para querer usar isso em vez de Node.js e TypeScript?
Se você não precisa espremer até a última gota de desempenho nem de layout e controle de memória, usar Zig traz mais desvantagens
Em CRUD, trabalho “enterprise” ou websites, você quase não vê vantagens no Zig
Um programa compilado em Zig pode ter apenas alguns KB e nenhuma dependência
Acesso a arrays escrito em uma linguagem de baixo nível pode ser otimizado com SIMD e paralelização, e pode ser várias ordens de grandeza mais rápido do que fazer a mesma coisa em JavaScript
A diferença é grande em processamento de texto, manipulação de imagens, processamento de vídeo, hashing etc.
Na prática, existem infinitos motivos para não usar JavaScript