Ejecución dinámica

Informar un problema Ver fuente

La ejecución dinámica es una función de Bazel desde la versión 0.21, en la que la ejecución local y remota de la misma acción se inician en paralelo mediante el resultado de la primera rama que finaliza, lo que cancela la otra rama. Combina la potencia de ejecución o la gran caché compartida de un sistema de compilación remoto con la baja latencia de la ejecución local, lo que proporciona lo mejor de ambos mundos para compilaciones limpias e incrementales.

En esta página, se describe cómo habilitar, ajustar y depurar la ejecución dinámica. Si tienes configurada la ejecución local y la remota, y tratas de 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 ve a la descripción general de la ejecución remota de Bazel.

¿Quieres habilitar la ejecución dinámica?

El módulo de ejecución dinámica es 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 marca --internal_spawn_scheduler a Bazel. Esto agrega una nueva estrategia de ejecución llamada dynamic. Ahora puedes usar esto como estrategia para los mnemónicos que desees ejecutar de forma dinámica, como --strategy=Javac=dynamic. Consulta la siguiente sección para saber cómo elegir para qué mnemotécnicos habilitar la ejecución dinámica.

Para cualquier mnemotécnico que usa la estrategia dinámica, las estrategias de ejecución remota se toman de la marca --dynamic_remote_strategy y las estrategias locales de la marca --dynamic_local_strategy. Pasar --dynamic_local_strategy=worker,sandboxed establece el valor predeterminado para que la rama local de ejecución dinámica pruebe con trabajadores o ejecución de zona de pruebas en ese orden. Pasar --dynamic_local_strategy=Javac=worker anula el valor predeterminado solo para la mnemotecnia de 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 realiza de forma remota como de costumbre y viceversa.

Si el sistema remoto tiene una caché, la marca --local_execution_delay agrega un retraso en milisegundos a la ejecución local después de que el sistema remoto indica un acierto de caché. Esto evita la ejecución local cuando es probable que haya más aciertos de caché. El valor predeterminado es 1,000 ms, pero se debe ajustar para que sea un poco más largo que lo que suelen tardar los aciertos de caché. El tiempo real depende tanto del sistema remoto como de cuánto tiempo tarde un recorrido 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 tiempo tardan los aciertos típicos de caché.

La ejecución dinámica se puede usar con la estrategia de zona de pruebas local y 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 pueden usar trabajadores multiplex. En los sistemas Darwin y Windows, la estrategia de zona de pruebas puede ser lenta. Puedes pasar --reuse_sandbox_directories para reducir la sobrecarga que implica crear zonas de pruebas en estos sistemas.

La ejecución dinámica también se puede ejecutar con la estrategia standalone, aunque como la estrategia standalone debe tomar el bloqueo de salida cuando comienza a ejecutarse, bloquea de manera efectiva la estrategia remota para que no finalice primero. La marca --experimental_local_lockfree_output permite solucionar este problema, ya que permite que la ejecución local escriba directamente en el resultado, pero que la ejecución remota pueda anular si eso finaliza primero.

Si una de las ramas de la ejecución dinámica finaliza primero, pero es una falla, toda la acción falla. Esta es una elección intencional para evitar que las diferencias entre la ejecución local y remota pasen desapercibidas.

Para obtener más información sobre cómo funcionan la ejecución dinámica y su bloqueo, consulta las excelentes entradas de blog de Julio Merino.

¿Cuándo debo usar la ejecución dinámica?

La ejecución dinámica requiere algún tipo de sistema de ejecución remota. En este momento, no es posible usar un sistema remoto que solo incluya caché, ya que un error 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 a nivel 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 cantidad de recursos de CPU y memoria, las acciones que no se incluyen en esas categorías solo retrasan 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 dedicado a 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ámico dedican mucho tiempo a adquirir recursos o a async-worker-finish, es posible que tengas algunas acciones locales lentas que retrasen los subprocesos de trabajo.

Cómo generar perfiles de datos con un rendimiento deficiente de ejecución dinámica

En el perfil anterior, que usa 8 trabajadores Javac, vemos que muchos trabajadores de Javac perdieron las carreras y terminaron su trabajo en los subprocesos async-worker-finish. Esto se debe a que un mnemónico que no es trabajador toma suficientes recursos para retrasar a los trabajadores.

Cómo crear perfiles de datos con un mejor rendimiento de ejecución dinámica

Cuando solo se ejecuta Javac con la ejecución dinámica, solo aproximadamente 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 mnemotécnicos, lo que a menudo generaría 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 solo se pueden mantener con algunas combinaciones específicas de ejecución local y remota. La --debug_spawn_scheduler agrega un resultado adicional del sistema de ejecución dinámico que puede ayudar a depurar estos problemas. También puedes ajustar la marca --local_execution_delay y la cantidad de trabajos remotos en comparación con los locales para facilitar la reproducción de los problemas.

Si tienes problemas con la ejecución dinámica mediante la estrategia standalone, intenta ejecutarla sin --experimental_local_lockfree_output o ejecuta tus acciones locales en la zona de pruebas. Esto puede ralentizar un poco tu compilación (consulta la sección anterior si usas Mac o Windows), pero quita algunas causas posibles de fallas.