HeadlinesBriefing favicon HeadlinesBriefing.com

Buildprof Visualiza Tempos de Compilação

Hacker News •
×

Criei o buildprof (GitHub), uma ferramenta open source que mostra onde o tempo é gasto quando você compila software em Linux. Aqui está um vídeo em tempo real dele perfilando uma compilação limpa de ripgrep: Assista à demonstração do buildprof. Às vezes, as compilações são lentas simplesmente porque há muito código para compilar. Mas mais frequentemente do que não, há problemas corrigíveis: paralelismo ruim, trabalho repetido, downloads de dependências ou uma enorme invocação de compilador/linker.

O buildprof deixa tudo claramente visível, para que você possa ver o que vale a pena investigar e otimizar. Você o executa colocando buildprof -- na frente de qualquer comando de build que já use: buildprof -- make -j16, buildprof -- cargo build, buildprof -- ninja -C out/target, buildprof -- just build, buildprof -- ./dev/custom-build-script.sh. O buildprof registra cada processo que seu comando de build lança, incluindo seus subprocessos (e seus subprocessos…), e os dispõe em uma linha do tempo.

O tempo move-se da esquerda para a direita, a largura da barra mostra a duração, e os processos filhos aparecem abaixo do que os lançou. Criei o buildprof porque este tweet de Jarred Sumner, arquiteto-chefe do tempo de execução do Bun, estava morando de graça na minha cabeça: Especificamente, a afirmação de que a nova compilação Rust do Bun era >5× mais rápida em Linux do que sua antiga compilação Zig. Na minha experiência, projetos Zig normalmente compilavam muito mais rápido que projetos Rust de complexidade semelhante.

Essa intuição foi o suficiente para me fazer sentir que havia um mistério a resolver. Isso foi ainda mais complicado por outro detalhe importante, mas facilmente esquecido, no tweet: a compilação Zig usava Full LTO, enquanto a compilação Rust usava Thin LTO. Os compiladores normalmente otimizam unidades de compilação separadas largely em isolamento.

A otimização em tempo de ligação (LTO) permite que eles otimizem através dessas fronteiras. Full LTO reúne essas unidades em um único grande trabalho de otimização, enquanto Thin LTO preserva mais separação de modo que grande parte do trabalho pode rodar em paralelo. Baseado na minha experiência passada, essa diferença pode ter um efeito enorme no tempo de compilação.

O tweet mencionou de passagem, mas eu me perguntei quanto da melhoria no título realmente explicava. Comecei tentando reproduzir os números. Os números se reproduziram.

Mas e agora? Baixei o Bun 1.3.14 e o Bun 1.4.0 e escrevi scripts para replay das suas compilações Linux x64 CI em uma VM Linux de 6 núcleos, 12 threads. Os scripts preservaram os passos de build e suas dependências, executando tudo em uma única máquina. Minhas medições estavam na mesma faixa que as do Jarred: Linux x64 build Zig era 30m06s, Rust era 5m37s.

Meu replay CI de uma única máquina 24m24s 5m40s. OK, então a brecha também apareceu na minha máquina. Mas muita coisa tinha mudado entre as duas medições além da linguagem; então o que era realmente responsável? Era o compilador Zig que estava levando todo esse tempo extra? Ou talvez fosse o link Full LTO.

Ou talvez houvesse algo mais na compilação do Bun que eu nem havia pensado em olhar. É aqui que entrou o meu cérebro de analista e ferramentas para desenvolvedores. Geralmente, quando estou tentando entender por que algo está lento, eu quero um trace: o que aconteceu, quando aconteceu e quanto tempo levou. Seria realmente legal ter isso para essas compilações, para colocá-las em uma linha do tempo e ver onde seu tempo realmente foi gasto.

Mas uma compilação envolve muitas ferramentas diferentes, cada uma com sua própria ideia do que está acontecendo. O que eu poderia registrar para poder ver através de todas elas? Os builds são árvores de processos. Quando você digita cargo build ou zig build, parece que você está executando um único programa.

O sistema de build descobre o que precisa ser reconstruído, a ordem entre essas peças e o que pode rodar em paralelo. Mas geralmente, ele não realiza todo esse trabalho ele mesmo; ele lança compiladores, geradores de código, arquivadores, linkers e scripts arbitrários. Estes podem lançar mais programas que lançam alguns mais...

Diferentes sistemas de build descrevem esse trabalho de formas diferentes. Cargo vê crates, Ninja vê bordes de build e CMake gera instruções para outro sistema de build. Desde o sistema operacional.