Execução dinâmica

Informar um problema Ver código-fonte Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

A execução dinâmica (link em inglês) é um recurso do Bazel em que a execução local e remota de a mesma ação são iniciadas em paralelo, usando a saída da primeira ramificação que termina, cancelando a outra ramificação. Ele combina o poder de execução e/ou grande cache compartilhado de um sistema de compilação remota com baixa latência de execução, oferecendo o melhor dos dois mundos para builds limpos e incrementais parecidos.

Esta página descreve como ativar, ajustar e depurar a execução dinâmica. Se você configurou a execução local e remota e está tentando ajustar as configurações do Bazel para melhorar o desempenho, esta página é para você. Se você ainda não tiver configuração da execução remota, acesse a página de Execução remota do Bazel Visão geral.

Ativando a execução dinâmica?

O módulo de execução dinâmica faz parte do Bazel, mas para usar o você já deve ser capaz de compilar local e remotamente a mesma configuração do Bazel.

Para ativar o módulo de execução dinâmica, transmita o --internal_spawn_scheduler para o Bazel. Isso adiciona uma nova estratégia de execução chamada dynamic. Agora você pode use isso como estratégia para as mnemônicas que você quer executar dinamicamente, como --strategy=Javac=dynamic: Confira a próxima seção para saber como escolher quais mnemônicas ativar a execução dinâmica.

Para qualquer mnemônica que use a estratégia dinâmica, as estratégias de execução remota são retirado da sinalização --dynamic_remote_strategy e as estratégias locais do sinalização --dynamic_local_strategy. Em aprovação --dynamic_local_strategy=worker,sandboxed define o padrão para o de execução dinâmica para testar com workers ou execução em sandbox ordem. A transmissão de --dynamic_local_strategy=Javac=worker substitui o padrão apenas para a mnemônica Javac. A versão remota funciona da mesma forma. As duas sinalizações podem ser especificado várias vezes. Se uma ação não pode ser executada localmente, ela executada remotamente como de costume e vice-versa.

Se o sistema remoto tiver um cache, a flag --dynamic_local_execution_delay adiciona um atraso em milissegundos à execução local depois que o sistema remoto indicou uma ocorrência em cache. Isso evita a execução local quando mais acertos de cache são prováveis. O valor padrão é 1.000 ms, mas deve ser ajustado para ser um pouco menor mais do que as ocorrências em cache costumam levar. O tempo real depende do sistema remoto e do tempo que uma ida e volta leva. Normalmente, o valor será o mesmo para todos os usuários de um determinado sistema remoto, a menos que alguns deles estejam longe o suficiente para aumentar a latência de ida e volta. É possível usar a criação de perfil do Bazel recursos para analisar quanto tempo o número de ocorrências em cache.

A execução dinâmica pode ser usada com a estratégia de sandbox local e com workers persistentes. Os workers persistentes são executados automaticamente com sandboxing quando usados com execução dinâmica e não podem usar workers múltiplos. Em sistemas Darwin e Windows, a estratégia de sandbox pode ser lenta. Você pode transmitir --reuse_sandbox_directories para reduzir o overhead da criação de sandboxes nesses sistemas.

A execução dinâmica também pode ser executada com a estratégia standalone. No entanto, como a estratégia standalone precisa usar o bloqueio de saída quando começa a ser executada, ela bloqueia efetivamente a estratégia remota para que ela não termine primeiro. A flag --experimental_local_lockfree_output permite contornar esse problema, permitindo que a execução local seja gravada diretamente na saída, mas seja abortada pela execução remota, caso ela termine primeiro.

Se uma das execuções dinâmicas terminar primeiro, mas falhar, a ação inteira falhará. Essa é uma escolha intencional para evitar diferenças entre a execução local e remota passe despercebida.

Para mais informações sobre como a execução dinâmica e o bloqueio funcionam, consulte as excelentes postagens do blog de Julio Merino.

Quando devo usar a execução dinâmica?

A execução dinâmica requer algum tipo de sistema de execução remota. No momento, não é possível usar um sistema remoto somente de cache, já que uma falha de cache seria considerada uma ação com falha.

Nem todos os tipos de ação são adequados para execução remota. Os melhores candidatos são aqueles que são inerentemente mais rápidos localmente, por exemplo, pelo uso de workers persistentes, ou aqueles que são executados rápido o suficiente para que a sobrecarga da execução remota domine o tempo de execução. Como cada ação executada localmente bloqueia uma quantidade de recursos de CPU e memória, a execução de ações que não se enquadram nessas categorias apenas atrasa a execução das que se enquadram.

A partir da versão 5.0.0-pre.20210708.4, o cenário de desempenho contém dados sobre a execução do worker, incluindo o tempo gasto para concluir uma solicitação de trabalho depois de perder uma corrida de execução dinâmica. Se as linhas de execução do worker de execução dinâmica forem exibidas passar muito tempo adquirindo recursos ou muito tempo no async-worker-finish, algumas ações locais lentas podem atrasar o worker fios

Criação de perfil de dados com baixo desempenho de execução dinâmica

No perfil acima, que usa oito workers Javac, muitos workers Javac perderam as corridas e terminaram o trabalho nas linhas de execução async-worker-finish. Isso foi causado por uma mnemônica não relacionada a workers que consumiu recursos suficientes para atrasar os workers.

Criar perfis de dados com melhor performance de execução dinâmica

Quando apenas Javac é executado com execução dinâmica, apenas cerca da metade do tempo iniciado trabalhadores acabam perdendo a corrida depois de iniciar o trabalho.

A flag --experimental_spawn_scheduler recomendada anteriormente foi descontinuada. Ela ativa a execução dinâmica e define dynamic como a estratégia padrão para todas as mnemônicas, o que geralmente levaria a esse tipo de problema.

Desempenho

A abordagem de execução dinâmica pressupõe que há recursos suficientes disponíveis local e remotamente que vale a pena gastar recursos extras para melhorar o desempenho geral. Mas o uso excessivo de recursos pode atrasar o Bazel ou na máquina em que é executado ou colocar uma pressão inesperada em um sistema remoto. Há várias opções para mudar o comportamento da execução dinâmica:

--dynamic_local_execution_delay atrasa o início de uma filial local em um número de milissegundos após o início da ramificação remota, mas somente se houver uma ocorrência em cache remoto durante a compilação atual. Assim, os builds que se beneficiam do armazenamento em cache remoto para não desperdiçar recursos locais quando for provável que a maioria e as saídas podem ser encontradas no cache. Dependendo da qualidade do cache, reduzir isso pode melhorar a velocidade de build, mas com o custo de usar mais recursos locais.

--experimental_dynamic_local_load_factor é uma opção experimental avançada de gerenciamento de recursos. Ele usa um valor de 0 a 1, sendo 0 a desativação desse recurso. Quando definido como um valor acima de 0, o Bazel ajusta o número de as ações programadas localmente quando muitas ações aguardam ser programados. Se ele for definido como 1, será possível programar quantas ações forem necessárias são CPUs disponíveis, de acordo com --local_cpu_resources. Valores mais baixos definem o número de ações programadas para um número proporcionalmente menor à medida que mais ações são disponíveis para execução. Isso pode parecer contra-intuitivo, mas com um bom sistema remoto, a execução local não ajuda muito quando muitas ações estão sendo executadas, e a CPU local é usada melhor para gerenciar ações remotas.

--experimental_dynamic_slow_remote_time prioriza o início de ramificações locais quando a ramificação remota está em execução há pelo menos esse tempo. Normalmente, a ação programada mais recentemente tem prioridade, porque tem a maior chance de ganhar a corrida. No entanto, se o sistema remoto às vezes travar ou demorar muito, isso pode fazer com que um build seja transferido. Essa opção não é ativada por padrão, porque pode ocultar problemas com o sistema remoto que precisam ser corrigidos. Confirme se para monitorar o desempenho do sistema remoto se você ativar essa opção.

--experimental_dynamic_ignore_local_signals pode ser usado para permitir que o controle remoto ramificação assume quando uma geração local é encerrada devido a um determinado sinal. Isso é útil principalmente com os limites de recursos do worker (consulte --experimental_worker_memory_limit_mb, --experimental_worker_sandbox_hardening e --experimental_sandbox_memory_limit_mb), em que as operações do worker podem ser encerradas quando usam muitos recursos.

O perfil de trace JSON contém um gráficos relacionados ao desempenho que podem ajudar a identificar maneiras de melhorar a do desempenho e do uso de recursos.

Solução de problemas

Problemas com a execução dinâmica podem ser sutis e difíceis de depurar, pois podem apenas sob algumas combinações específicas de execução local e remota. --debug_spawn_scheduler adiciona outra saída da execução dinâmica. que pode ajudar a depurar esses problemas. Também é possível ajustar a flag --dynamic_local_execution_delay e o número de jobs remotos e locais para facilitar a reprodução dos problemas.

Se você tiver problemas com a execução dinâmica usando a estratégia standalone, tente executar sem --experimental_local_lockfree_output ou execute suas ações locais em um ambiente de teste. Isso pode deixar seu build um pouco mais lento (confira acima se você estiver usando Mac ou Windows), mas remove algumas causas possíveis de falhas.