La ejecución dinámica es una función de Bazel desde la versión 0.21, en la que se inician en paralelo la ejecución local y remota de la misma acción, se usa el resultado de la primera rama que finaliza y se cancela la otra rama. Combina la potencia de ejecución o la caché compartida grande de un sistema de compilación remota con la baja latencia de la ejecución local, lo que proporciona lo mejor de ambos mundos para compilaciones limpias y también incrementales.
En esta página, se describe cómo habilitar, ajustar y depurar la ejecución dinámica. Si tienes configuradas la ejecución local y remota, y quieres ajustar la configuración de Bazel para obtener un mejor rendimiento, esta página es para ti. Si aún no tienes configurada la ejecución remota, primero consulta la Descripción general de la ejecución remota de Bazel.
¿Cómo habilitar la ejecución dinámica?
El módulo de ejecución dinámica forma parte de Bazel, pero para usar la ejecución dinámica, ya debes poder compilar de forma local y remota desde la misma configuración de Bazel.
Para habilitar el módulo de ejecución dinámica, pasa la --internal_spawn_scheduler
marca a Bazel. Esto agrega una nueva estrategia de ejecución llamada dynamic. Ahora puedes
usarla como estrategia para los mnemónicos que deseas ejecutar de forma dinámica, como
--strategy=Javac=dynamic. Consulta la siguiente sección para elegir los mnemónicos
para los que habilitar la ejecución dinámica.
Para cualquier mnemónico que use la estrategia dinámica, las estrategias de ejecución remota se
toman de la marca --dynamic_remote_strategy y las estrategias locales de la
--dynamic_local_strategy marca. Si pasas
--dynamic_local_strategy=worker,sandboxed, se establece el valor predeterminado para que la rama local
de la ejecución dinámica intente con trabajadores o ejecución en zona de pruebas en ese
orden. Si pasas --dynamic_local_strategy=Javac=worker, se anula el valor predeterminado para
el mnemónico Javac. La versión remota funciona de la misma manera. Ambas marcas se pueden
especificar varias veces. Si una acción no se puede ejecutar de forma local, se
ejecuta de forma remota como de costumbre y viceversa.
Si tu sistema remoto tiene una caché, la --local_execution_delay
marca agrega una demora en milisegundos a la ejecución local después de que el sistema remoto
haya indicado un acierto de caché. Esto evita ejecutar la ejecución local cuando es más probable que haya aciertos de caché. El valor predeterminado es de 1,000 ms, pero se debe ajustar para que sea solo
un poco más largo que lo que suelen tardar los aciertos de caché. El tiempo real depende del
sistema remoto y de cuánto tarda un viaje de ida y vuelta. Por lo general, el valor será el
mismo para todos los usuarios de un sistema remoto determinado, a menos que algunos de ellos estén lo suficientemente lejos
como para agregar latencia de ida y vuelta. Puedes usar las
funciones de generación de perfiles de Bazel
para ver cuánto tardan los aciertos de caché típicos.
La ejecución dinámica se puede usar con la estrategia local en zona de pruebas, así como con
trabajadores persistentes. Los trabajadores persistentes se ejecutarán automáticamente con la zona de pruebas cuando se usen con la ejecución dinámica y no podrán usar trabajadores multiplexados. En los sistemas Darwin y Windows,
la estrategia en zona de pruebas puede ser lenta. Puedes pasar
--reuse_sandbox_directories para reducir la sobrecarga de crear zonas de pruebas en estos sistemas.
La ejecución dinámica también se puede ejecutar con la estrategia standalone, aunque, como la
standalone estrategia debe tomar el bloqueo de salida cuando comienza a ejecutarse, la
bloquea de manera efectiva la estrategia remota para que no termine primero. La marca
--experimental_local_lockfree_output permite solucionar este problema, ya que
permite que la ejecución local escriba directamente en la salida, pero que la ejecución remota la anule si termina primero.
Si una de las ramas de la ejecución dinámica termina primero, pero falla, la acción completa falla. Esta es una elección intencional para evitar que pasen desapercibidas las diferencias entre la ejecución local y remota.
Para obtener más información sobre cómo funciona la ejecución dinámica y su bloqueo, consulta las excelentes entradas de blog de Julio Merino.
¿Cuándo se debe usar la ejecución dinámica?
La ejecución dinámica requiere algún tipo de sistema de ejecución remota. Actualmente, no es posible usar un sistema remoto solo de caché, ya que una falta de caché se consideraría una acción fallida.
No todos los tipos de acciones son adecuados para la ejecución remota. Los mejores candidatos son aquellos que son inherentemente más rápidos de forma local, por ejemplo, mediante el uso de trabajadores persistentes, o aquellos que se ejecutan lo suficientemente rápido como para que la sobrecarga de la ejecución remota domine el tiempo de ejecución. Dado que cada acción ejecutada de forma local bloquea una cierta cantidad de recursos de CPU y memoria, ejecutar acciones que no entran en esas categorías solo retrasa la ejecución de las que sí lo hacen.
A partir de la versión
5.0.0-pre.20210708.4,
la generación de perfiles de rendimiento
contiene datos sobre la ejecución del trabajador, incluido el tiempo que se tarda en finalizar una solicitud de trabajo
después de perder una carrera de ejecución dinámica. Si ves que los subprocesos de trabajo de ejecución dinámica
pasan mucho tiempo adquiriendo recursos o mucho tiempo
en el async-worker-finish, es posible que tengas algunas acciones locales lentas que retrasen los
subprocesos de trabajo.
En el perfil anterior, que usa 8 trabajadores de Javac, vemos que muchos trabajadores de Javac
perdieron las carreras y terminaron su trabajo en los async-worker-finish
subprocesos. Esto se debió a que un mnemónico no trabajador tomó suficientes recursos para
retrasar a los trabajadores.
Cuando solo se ejecuta Javac con ejecución dinámica, solo alrededor de la mitad de los trabajadores iniciados terminan perdiendo la carrera después de comenzar su trabajo.
La marca --experimental_spawn_scheduler recomendada anteriormente dejó de estar disponible.
Activa la ejecución dinámica y establece dynamic como la estrategia predeterminada para todos los
mnemónicos, lo que a menudo generaba este tipo de problemas.
Solución de problemas
Los problemas con la ejecución dinámica pueden ser sutiles y difíciles de depurar, ya que pueden
manifestarse solo en algunas combinaciones específicas de ejecución local y remota.
La marca --debug_spawn_scheduler agrega un resultado adicional del sistema de ejecución dinámica
que puede ayudar a depurar estos problemas. También puedes ajustar la
--local_execution_delay marca y la cantidad de trabajos remotos en comparación con los locales
para que sea más fácil reproducir los problemas.
Si tienes problemas con la ejecución dinámica con la standalone
estrategia, intenta ejecutar sin --experimental_local_lockfree_output, o ejecuta
tus acciones locales en zona de pruebas. Esto puede ralentizar un poco tu compilación (consulta lo anterior si
usas Mac o Windows), pero quita algunas posibles causas de fallas.