动态执行是 Bazel 中的一项功能 自 0.21 版起, 该功能可并行启动同一操作的本地执行和远程执行, 并使用第一个完成的分支的输出,取消另一个 分支。它将远程 构建系统的执行能力和/或大型共享缓存与本地执行的低延迟相结合,为干净构建和增量构建提供了两全其美的解决方案。
本页介绍了如何启用、调整和调试动态执行。如果您同时设置了本地执行和远程执行,并且正在尝试调整 Bazel 设置以获得更好的性能,那么本页非常适合您。如果您尚未设置 远程执行,请先参阅 Bazel 远程执行概览。
启用动态执行?
动态执行模块是 Bazel 的一部分,但如需使用动态 执行,您必须能够从同一 Bazel 设置在本地和远程进行编译。
如需启用动态执行模块,请将 --internal_spawn_scheduler
标志传递给 Bazel。这会添加一个名为 dynamic 的新执行策略。现在,您可以将此策略用作要动态运行的助记符的策略,例如
--strategy=Javac=dynamic。如需了解如何选择要为其启用动态执行的助记符
,请参阅下一部分。
对于使用动态策略的任何助记符,远程执行策略取自 --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开始,
性能分析
包含有关工作器执行的数据,包括在动态执行竞争失败后完成工作
请求所花费的时间。如果您看到动态执行
工作器线程花费大量时间获取资源,或者在
async-worker-finish中花费大量时间,则可能是某些本地操作速度较慢,导致
工作器线程延迟。
在上面的配置文件中,我们使用了 8 个 Javac 工作器,可以看到许多 Javac 工作器
在竞争中失败,并在 async-worker-finish
线程上完成了工作。这是由非工作器助记符占用足够的资源来
延迟工作器造成的。
当仅使用动态执行运行 Javac 时,只有大约一半已启动的 工作器在开始工作后最终在竞争中失败。
之前推荐的 --experimental_spawn_scheduler 标志已废弃。
它会启用动态执行并将 dynamic 设置为所有
助记符的默认策略,这通常会导致此类问题。
问题排查
动态执行的问题可能很微妙且难以调试,因为它们可能仅在本地执行和远程执行的某些特定组合下才会显现出来。--debug_spawn_scheduler 会添加来自动态
执行系统的额外输出,有助于调试这些问题。您还可以调整
--local_execution_delay 标志以及远程作业与本地作业的数量
,以便更轻松地重现问题。
如果您在使用 standalone
策略时遇到动态执行问题,请尝试在不使用 --experimental_local_lockfree_output 的情况下运行,或者在沙盒中运行
本地操作。这可能会稍微减慢构建速度(如果您使用的是 Mac 或 Windows,请参阅上文),但会消除一些可能导致失败的原因。