Otimizar a velocidade de iteração

Nesta página, descrevemos como otimizar a performance de build do Bazel ao executá-lo repetidamente.

Estado do ambiente de execução do Bazel

Uma invocação do Bazel envolve várias partes que interagem.

  • A interface de linha de comando (CLI, na sigla em inglês) do bazel é a ferramenta de front-end voltada ao usuário e recebe comandos dele.

  • A ferramenta de CLI inicia um servidor do Bazel para cada base de saída distinta. O servidor do Bazel geralmente é persistente, mas é desativado após algum tempo de inatividade para não desperdiçar recursos.

  • O servidor do Bazel executa as etapas de carregamento e análise de um determinado comando (build, run, cquery, etc.), em que ele constrói as partes necessárias do gráfico de build na memória. As estruturas de dados resultantes são mantidas em o servidor do Bazel como parte do cache de análise.

  • O servidor do Bazel também pode executar a ação ou enviar ações para execução remota, se estiver configurado para isso. Os resultados das execuções de ações também são armazenados em cache, ou seja, no cache de ações (ou cache de execução, que pode ser local ou remoto e compartilhado entre servidores do Bazel).

  • O resultado da invocação do Bazel é disponibilizado na árvore de saída.

Executar o Bazel de forma iterativa

Em um fluxo de trabalho típico do desenvolvedor, é comum criar (ou executar) um trecho de código repetidamente, geralmente com uma frequência muito alta (por exemplo, para resolver um erro de compilação ou investigar um teste com falha). Nessa situação, é importante que as invocações repetidas de bazel tenham o menor overhead possível em relação a ação repetida subjacente (por exemplo, invocar um compilador ou executar um teste).

Considerando isso, vamos analisar novamente o estado do ambiente de execução do Bazel:

O cache de análise é uma parte essencial dos dados. Uma quantidade significativa de tempo pode ser gasto apenas nas fases de carregamento e análise de uma execução a frio (ou seja, uma execução logo após o início do servidor do Bazel ou quando o cache de análise foi descartado). Para um único build a frio bem-sucedido (por exemplo, para uma versão de produção), esse custo é tolerável, mas para criar repetidamente o mesmo destino, é importante que esse custo seja amortizado e não repetido em cada invocação.

O cache de análise é bastante volátil. Primeiro, ele faz parte do estado no processo do servidor do Bazel. Portanto, perder o servidor significa perder o cache. No entanto, o cache também é invalidado com muita facilidade. Por exemplo, muitas bazel flags de linha de comando fazem com que o cache seja descartado. Isso ocorre porque muitas flags afetam o gráfico de build (por exemplo, devido a atributos configuráveis). Algumas mudanças de flag também podem fazer com que o servidor do Bazel seja reiniciado (por exemplo, mudar as opções de inicialização).

Um bom cache de execução também é valioso para a performance de build. Um cache de execução pode ser mantido localmente no disco ou remotamente. O cache pode ser compartilhado entre servidores do Bazel e, de fato, entre desenvolvedores.

Evitar descartar o cache de análise

O Bazel vai imprimir um aviso se o cache de análise for descartado ou se o servidor for reiniciado. Qualquer uma dessas ações deve ser evitada durante o uso iterativo:

  • Tenha cuidado ao mudar as bazel flags no meio de um fluxo de trabalho iterativo. Por exemplo, misturar um bazel build -c opt com um bazel cquery faz com que cada comando descarte o cache de análise do outro. Em geral, tente usar um conjunto fixo de flags durante um fluxo de trabalho específico.

  • Perder o servidor do Bazel significa perder o cache de análise. O servidor do Bazel tem um tempo de inatividade configurável, após o qual é desativado. Você pode configurar esse tempo pelo arquivo bazelrc de acordo com suas necessidades. O servidor também é reiniciado quando as flags de inicialização mudam. Portanto, evite mudar essas flags, se possível.

  • Cuidado: o servidor do Bazel é encerrado se você pressionar Ctrl-C repetidamente enquanto o Bazel estiver em execução. É tentador tentar economizar tempo interrompendo um build em execução que não é mais necessário, mas pressione Ctrl-C apenas uma vez para solicitar um encerramento normal da invocação atual.

  • Se você quiser usar vários conjuntos de flags do mesmo espaço de trabalho, pode usar várias bases de saída distintas, alternadas com a --output_base flag. Cada base de saída recebe o próprio servidor do Bazel.

Para que essa condição seja um erro em vez de um aviso, use a --noallow_analysis_cache_discard flag (introduzida no Bazel 6.4.0).