细分构建性能

报告问题 查看来源 每晚 · 7.2。 · 7.1敬上 · 7.0 · 6.5 · 6.4

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 所完成的工作量,而这反过来又会影响其性能。

构建请求的大小可能会对构建产生重大影响 性能版本越大可能意味着分析和 构建构建图。版本自然增长 随着添加/创建的依赖项越来越多,复杂性也随之增加 构建成本也会越来越高。

我们可以将此问题分解为不同的构建阶段,并使用以下 作为每个阶段所完成工作的替代指标:

  1. PackageMetrics.packages_loaded:已成功加载的软件包数量。 这里的回归表示读取和解析需要完成更多工作 在加载阶段添加的每个 BUILD 文件。

    • 这通常是由于添加了依赖项, 传递闭包。
    • 使用 query / cquery 查找 可能添加了新的依赖项的位置
  2. TargetMetrics.targets_configured:表示目标数量和 在 build 中配置的切面。回归表示 构造和遍历已配置的目标图表。

    • 这通常是由于添加了依赖项,并且必须构建 传递闭包关系图。
    • 使用 cquery 查找新位置 依赖项。
  3. ActionSummary.actions_created:表示在 build 中创建的操作。 而回归则表示构建动作图的工作量更大。注意事项 其中还包括可能尚未执行的未使用的操作。

  4. ActionSummary.actions_executed:执行的操作数, 回归直接表示执行这些操作需要更多工作。

    • BEP 会输出操作统计信息 显示执行次数最多的操作类型的 ActionData。默认情况下 它收集了最热门的 20 个操作类型,但您可以将 --experimental_record_metrics_for_all_mnemonics 来收集所执行的所有操作类型的此类数据。
    • 这应该可以帮助您确定执行了哪些操作 (此外)。
  5. BuildGraphSummary.outputArtifactCount: 已执行操作

    • 如果执行的操作数没有增加,则很可能是 规则实施已更改。

这些指标都受本地缓存状态的影响, 您需要确保从中提取这些指标的 build 是 全新 build

我们已经注意到,如果上述任一指标出现衰退,都可能 实际用时、CPU 时间和内存用量降低。

本地资源的使用

Bazel 会使用本地机器上的各种资源(既是为了分析 构建图并驱动执行,以及用于运行本地操作), 可能会影响机器在执行 构建,以及执行其他任务。

所用时间

最容易受到干扰的指标可能是 就是时间;尤其是实际用时、CPU 时间和系统时间您可以 使用 bazel-bench 获取 是这些指标的基准,通过足够的--runs,您可以 提高衡量结果的统计显著性。

  • 实际用时是实际经过的时间。

    • 如果只有实际用时回归,我们建议您收集 JSON 轨迹配置文件和查找 差异。否则,删除容器可能会更高效 调查其他回归指标,因为它们可能对数据墙造成了影响, 。
  • CPU 时间是 CPU 执行用户代码所用的时间。

    • 如果两次项目提交期间的 CPU 时间回归,我们建议收集 以及 Starlark CPU 配置文件您可能还需要使用 --nobuild 来 将构建限制在分析阶段,因为这是大部分 完成 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 infoBEP

  • bazel info used-heap-size-after-gc:之后的已使用内存量(以字节为单位) 对 System.gc() 的调用。

    • Bazel 台 也可为此指标提供基准数据
    • 此外,还有 peak-heap-sizemax-heap-sizeused-heap-sizecommitted-heap-size(请参阅 文档),但 不太相关。
  • 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。 这样可以避免下载不必要的中间工件,从而加快构建速度。

另一种方法是 要节省的磁盘缓存 下载带宽。