动态执行

报告问题 查看源代码 每夜 build · 8.0 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

动态执行是 Bazel 从 0.21 版开始提供的一项功能,它会并行启动同一操作的本地和远程执行,并使用第一个完成分支的输出,取消其他分支。它将远程构建系统的执行能力和/或大型共享缓存与本地执行的低延迟性相结合,为干净构建和增量构建提供了两全其美。

本页介绍了如何启用、调整和调试动态执行。如果您同时设置了本地和远程执行,并尝试调整 Bazel 设置以提升性能,请参阅本页面。如果您尚未设置远程执行,请先参阅 Bazel 远程执行概览

启用动态执行?

动态执行模块是 Bazel 的一部分,但若要使用动态执行,您必须已经能够通过同一 Bazel 设置在本地和远程进行编译。

如需启用动态执行模块,请将 --internal_spawn_scheduler 标志传递给 Bazel。这会添加一个名为 dynamic 的新执行策略。现在,您可以将其用作要动态运行的助记符(例如 --strategy=Javac=dynamic)的策略。如需了解如何选择要为哪些助记符启用动态执行,请参阅下一部分。

对于使用动态策略的任何 mnemonic,远程执行策略取自 --dynamic_remote_strategy 标志,本地策略取自 --dynamic_local_strategy 标志。传递 --dynamic_local_strategy=worker,sandboxed 会为动态执行的本地分支设置默认值,以便按顺序尝试使用工作器或沙盒化执行。传递 --dynamic_local_strategy=Javac=worker 仅会替换 Javac 助记符的默认值。远程版本的工作方式与此相同。这两个标志可以多次指定。如果某个操作无法在本地执行,则会照常在远程执行,反之亦然。

如果您的远程系统具有缓存,--local_execution_delay 标志会在远程系统指示缓存命中后,向本地执行添加毫秒级延迟。这样可以避免在可能有更多缓存命中的情况下运行本地执行。默认值为 1000 毫秒,但应调整为比缓存命中通常所需时间稍长一些。实际时间取决于远程系统和往返所需时间。通常,给定远程系统的所有用户的此值都相同,除非其中一些用户距离足够远,以致增加了往返延迟时间。您可以使用 Bazel 性能分析功能来查看典型缓存命中所需的时间。

动态执行可与本地沙盒化策略以及永久性工作器搭配使用。持久性工作器与动态执行搭配使用时,将自动在沙盒中运行,并且无法使用多工作器。在 Darwin 和 Windows 系统上,沙盒策略可能会运行缓慢;您可以传递 --reuse_sandbox_directories 以减少在这些系统上创建沙盒的开销。

动态执行也可以与 standalone 策略一起运行,但由于 standalone 策略在开始执行时必须获取输出锁,因此它会有效阻止远程策略先完成。--experimental_local_lockfree_output 标志可通过允许本地执行直接写入输出,但如果远程执行先完成,则由远程执行中止,从而解决此问题。

如果动态执行的某个分支先完成但失败,则整个操作都会失败。这是有意为之,以免本地和远程执行之间的差异不被注意到。

如需详细了解动态执行及其锁定的工作原理,请参阅 Julio Merino 的优秀博文

何时应使用动态执行?

动态执行需要某种形式的远程执行系统。目前无法使用仅限缓存的远程系统,因为缓存未命中会被视为操作失败。

并非所有类型的操作都适合远程执行。最佳候选项是那些在本地运行速度本质上更快的任务(例如通过使用永久性工作器),或者运行速度足够快,以至于远程执行开销占据了大部分执行时间的任务。由于每个在本地执行的操作都会锁定一定数量的 CPU 和内存资源,因此运行不属于这些类别的操作只会延迟属于这些类别的操作的执行。

从版本 5.0.0-pre.20210708.4 开始,性能分析包含有关 worker 执行的数据,包括在输掉动态执行争用后完成工作请求所花费的时间。如果您发现动态执行工作器线程花费大量时间获取资源,或者在 async-worker-finish 中花费大量时间,则可能是因为某些运行缓慢的本地操作延迟了工作器线程。

动态执行性能不佳的数据分析

在上面的配置文件中,我们使用了 8 个 Javac worker,其中许多 Javac worker 都输掉了争用,并在 async-worker-finish 线程中完成了工作。这是因为非工作器助记符占用了足够的资源,导致工作器延迟。

性能更出色的动态执行性能数据分析

仅使用动态执行运行 Javac 时,只有大约一半的已启动工作器在开始工作后最终会输掉竞态。

之前推荐的 --experimental_spawn_scheduler 标志已废弃。它会启用动态执行,并将 dynamic 设置为所有记忆法符号的默认策略,这通常会导致此类问题。

问题排查

动态执行方面的问题可能很细微且难以调试,因为它们可能仅在本地和远程执行的某些特定组合下才会出现。--debug_spawn_scheduler 会从动态执行系统添加额外输出,这些输出有助于调试这些问题。您还可以调整 --local_execution_delay 标志以及远程作业与本地作业的数量,以便更轻松地重现问题。

如果您在使用 standalone 策略进行动态执行时遇到问题,请尝试在不使用 --experimental_local_lockfree_output 的情况下运行,或在沙盒中运行本地操作。这可能会稍微减慢 build 速度(如果您使用的是 Mac 或 Windows,请参阅上文),但可以消除一些可能导致失败的原因。