Bazel 非常复杂,在构建过程中要执行很多不同的操作 其中一些可能会影响构建性能。此页面尝试映射 了解一些 Bazel 概念对构建性能的影响。虽然 我们提供了一些示例来说明如何检测构建性能, 提取指标导致的问题, 以及相应的修复方法。我们希望您能运用这些概念 。
干净 build 与增量 build
干净 build 是指从头开始构建所有内容,而增量构建 build 会重复使用一些已经完成的工作。
我们建议分别查看整洁 build 和增量 build,尤其是在 您正在收集 / 汇总的指标 Bazel 的缓存(例如 构建请求大小指标 )。它们还代表了两种不同的用户体验。相比于起始日期 从头开始干净构建(由于冷缓存,需要更长时间)、增量 随着开发者迭代代码(通常 因为缓存通常已经温暖)。
您可以使用 BEP 中的 CumulativeMetrics.num_analyses
字段进行分类
build。如果为 num_analyses <= 1
,则为干净 build;否则,我们大体上可以
分类为可能是增量 build - 用户原本可能已经
不同的标志或不同目标,从而导致有效整洁的构建。不限
对增量的严格定义可能需要表现为
启发词语,例如查看已加载的软件包数量
(PackageMetrics.packages_loaded
)。
确定性 build 指标(可作为 build 性能的代理)
由于构建性能的不确定性,衡量构建性能可能会比较困难 (例如,Bazel 的 CPU 时间或遥控器上的队列时间) )。因此,使用确定性指标作为 Bazel 所完成的工作量,而这反过来又会影响其性能。
构建请求的大小可能会对构建产生重大影响 性能版本越大可能意味着分析和 构建构建图。版本自然增长 随着添加/创建的依赖项越来越多,复杂性也随之增加 构建成本也会越来越高。
我们可以将此问题分解为不同的构建阶段,并使用以下 作为每个阶段所完成工作的替代指标:
PackageMetrics.packages_loaded
:已成功加载的软件包数量。 这里的回归表示读取和解析需要完成更多工作 在加载阶段添加的每个 BUILD 文件。TargetMetrics.targets_configured
:表示目标数量和 在 build 中配置的切面。回归表示 构造和遍历已配置的目标图表。- 这通常是由于添加了依赖项,并且必须构建 传递闭包关系图。
- 使用 cquery 查找新位置 依赖项。
ActionSummary.actions_created
:表示在 build 中创建的操作。 而回归则表示构建动作图的工作量更大。注意事项 其中还包括可能尚未执行的未使用的操作。- 使用 aquery 调试回归;
我们建议从
--output=summary
然后再进一步展开细目--skyframe_state
。
- 使用 aquery 调试回归;
我们建议从
ActionSummary.actions_executed
:执行的操作数, 回归直接表示执行这些操作需要更多工作。- BEP 会输出操作统计信息
显示执行次数最多的操作类型的
ActionData
。默认情况下 它收集了最热门的 20 个操作类型,但您可以将--experimental_record_metrics_for_all_mnemonics
来收集所执行的所有操作类型的此类数据。 - 这应该可以帮助您确定执行了哪些操作 (此外)。
- BEP 会输出操作统计信息
显示执行次数最多的操作类型的
BuildGraphSummary.outputArtifactCount
: 已执行操作- 如果执行的操作数没有增加,则很可能是 规则实施已更改。
这些指标都受本地缓存状态的影响, 您需要确保从中提取这些指标的 build 是 全新 build。
我们已经注意到,如果上述任一指标出现衰退,都可能 实际用时、CPU 时间和内存用量降低。
本地资源的使用
Bazel 会使用本地机器上的各种资源(既是为了分析 构建图并驱动执行,以及用于运行本地操作), 可能会影响机器在执行 构建,以及执行其他任务。
所用时间
最容易受到干扰的指标可能是
就是时间;尤其是实际用时、CPU 时间和系统时间您可以
使用 bazel-bench 获取
是这些指标的基准,通过足够的--runs
,您可以
提高衡量结果的统计显著性。
实际用时是实际经过的时间。
- 如果只有实际用时回归,我们建议您收集 JSON 轨迹配置文件和查找 差异。否则,删除容器可能会更高效 调查其他回归指标,因为它们可能对数据墙造成了影响, 。
CPU 时间是 CPU 执行用户代码所用的时间。
- 如果两次项目提交期间的 CPU 时间回归,我们建议收集
以及 Starlark CPU 配置文件您可能还需要使用
--nobuild
来 将构建限制在分析阶段,因为这是大部分 完成 CPU 繁重工作。
- 如果两次项目提交期间的 CPU 时间回归,我们建议收集
以及 Starlark CPU 配置文件您可能还需要使用
系统时间是 CPU 在内核中花费的时间。
- 如果系统时间回归,则主要与 Bazel 读取时 I/O 相关联 文件。
系统级负载分析
使用
--experimental_collect_load_average_in_profiler
Bazel 6.0 中引入的
JSON 轨迹分析器会收集
调用期间的系统负载平均值。
图 1. 包含系统平均负载的配置文件。
Bazel 调用期间的高负载可能表明 Bazel 安排了
您机器的并行本地操作过多。你可能需要了解
正在调整
--local_cpu_resources
和--local_ram_resources
尤其是在容器环境中(至少
#16512 合并)。
监控 Bazel 内存使用情况
获取 Bazel 的内存用量主要有两个来源:Bazel info
和
BEP。
bazel info used-heap-size-after-gc
:之后的已使用内存量(以字节为单位) 对System.gc()
的调用。BEP
MemoryMetrics.peak_post_gc_heap_size
:峰值 JVM 堆大小 GC 后的字节(需要设置--memory_profile
或尝试强制执行完全垃圾回收)。
内存使用量回归通常是由于 build 请求大小指标、 这通常是由于添加依赖项或更改规则 实施。
若要更精细地分析 Bazel 的内存占用量,我们建议您使用 内置内存分析器 。
永久性工作器的内存性能分析
而永久性工作器有助于加快构建速度
其内存占用量可能会显著增加(尤其是对于解释型语言),
可能存在问题Bazel 会收集其工作器的指标,特别是
WorkerMetrics.WorkerStats.worker_memory_in_kb
字段会指明
(通过助记符)。
JSON 轨迹分析器还
在调用期间收集永久性工作器内存用量,方法是传入
--experimental_collect_system_network_usage
标志(Bazel 6.0 中的新功能)。
图 2. 包含工作器内存用量的配置文件。
调低
--worker_max_instances
(默认值为 4)可能有助于减少
永久性工作器使用的内存量。我们正在积极努力
让 Bazel 的资源管理器和调度程序更加智能
。
监控远程构建的网络流量
在远程执行中,Bazel 会下载 执行操作因此,网络带宽可能会影响性能 。
如果您对构建使用远程执行,则可能需要考虑
使用
BEP 中的 NetworkMetrics.SystemNetworkStats
proto
(需要传递 --experimental_collect_system_network_usage
)。
此外,JSON 轨迹配置文件
让您可以在构建过程中查看系统级网络使用情况
方法是传递 --experimental_collect_system_network_usage
标志(Bazel 中的新功能)
6.0)。
图 3. 包含系统级网络使用情况的配置文件。
使用远程执行时,网络使用量较高但比较平缓可能表示
该网络就是您构建的瓶颈如果您尚未使用
可以考虑在不使用字节的情况下启用“构建”,方法是将
--remote_download_minimal
。
这样可以避免下载不必要的中间工件,从而加快构建速度。
另一种方法是 要节省的磁盘缓存 下载带宽。