1 pontos por GN⁺ 2 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • 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);
}
  • Novo código:
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

 
GN⁺ 2 시간 전
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

    • Ainda não dá para chamar de “muito eficiente”. Io ainda usa despacho dinâmico e há vários níveis de indireção
      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
    • Achei meio difícil entender como funciona o novo mecanismo de entrada/saída, especialmente o async/await
      Suponho que, ao chamar io.async numa implementação de IO baseada em event loop, ele internamente inicia algo como uma “task”, e o future seria o handle dela
      A 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?
    • Talvez um dia eu possa usar esses recursos novos, mas por questões de estabilidade e de alvos menos testados, acabo usando .use_llvm = true no build de Zig
  • Depois 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”

    • Dizer que ela não impõe um jeito “correto” de usar é discutível, já que não permite variáveis não usadas e não tem comentários de várias linhas
      Para mim isso é um problema de produtividade bem grande
    • É tão boa assim mesmo?
      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?
    • Meu principal bloqueio nisso é que, para mim, Mojo parece que vai virar a linguagem padrão para experimentar
    • Sem dúvida é uma ótima linguagem para experimentar, mas a equipe e a comunidade de Zig têm preferências muito fortes sobre a forma “correta” de usar a linguagem
  • Depois de ver um vídeo de entrevista com Andrew Kelley, fiquei com vontade de aprender Zig: https://www.youtube.com/watch?v=iqddnwKF8HQ

    • Respeito muito o Andrew e também gosto de usar Zig, mas aquela entrevista foi péssima
      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

    • A ideia é interessante, mas eu penso o contrário
      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
    • Pelo que sei, o SpaCy é desenvolvido assim
      Os modelos são implementados em Cython e a API voltada ao usuário é oferecida em Python
    • C# chega bem perto desse objetivo
    • Dá para simplesmente usar uma linguagem como Rust, que faz bem tanto programação de alto nível quanto de baixo nível
      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
    • Uma combinação comum para isso era C com Lua
      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

    • Pela minha experiência, eu diria que isso ainda está mais próximo de uma meta na maior parte do tempo
      É 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 allow
    • Mesmo criando um único arquivo com um teste dummy e executando zig test file.zig -OReleaseSafe, no meu computador isso leva alguns segundos
      E 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
    • Felizmente, a velocidade de compilação estilo anos 90 está voltando aos poucos
  • 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?

    • Invejo esse jeito de pensar. Se eu fosse assim também, provavelmente já teria lançado vários apps
    • Recomendo experimentar por um tempo o Ghostty, escrito em Zig, e compará-lo com um terminal como o Hyper, escrito em JavaScript
    • Não é porque os dois servem para áreas completamente diferentes?
    • Se o que você faz combina bem com Node e TypeScript, acho que não há motivo para usar Zig além de aprendizado ou curiosidade
      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
    • Em sistemas embarcados, falta memória a ponto de não dar para rodar Node
      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