Bazel 构建性能指标提取全指南:BEP、查询命令、Trace Profile、执行日志与基准测试
2026/9/12 8:30:29 网站建设 项目流程

Bazel 构建性能指标提取全指南:BEP、查询命令、Trace Profile、执行日志与基准测试

【免费下载链接】bazela fast, scalable, multi-language and extensible build system项目地址: https://gitcode.com/GitHub_Trending/ba/bazel

构建变慢是几乎每个 Bazel 用户都会遇到的问题。对于高频迭代的核心开发目标、被大量目标依赖的公共库,或是自定义规则等代表性目标,提升单次构建性能的价值尤为突出。而改进性能的第一步,是搞清楚资源究竟消耗在哪里。本文基于 Bazel 官方文档整理了一套完整的构建性能指标采集体系:Build Event Protocol(BEP)、query/cquery/aquery 查询命令、JSON Trace Profile、Execution Log、Execution Graph Log,以及 bazel-bench 基准测试工具。读完本文,你将掌握每种指标采集途径的适用场景、关键参数与实现原理,能够针对自己的构建建立性能基线并定位瓶颈。

为什么要采集构建性能指标

Bazel 构建流程由加载(loading)、分析(analysis)和执行(execution)等多个阶段组成,耗时可能来自包加载、规则分析、action 执行调度、I/O 与 GC 等不同环节。仅有"构建很慢"的笼统感知是不够的,需要把时间拆解到具体阶段和具体 action,才能确定优化方向。官方建议优先关注三类目标:

  1. 核心开发者目标:被高频迭代和重建的目标,其单次构建的每次提速都会反复兑现。
  2. 被广泛依赖的公共库:它们的编译时间会传导到下游大量目标。
  3. 某类目标的代表(如自定义规则):诊断并修复一次构建中的问题,往往能推广到更大规模。

采集指标正是为了回答"资源花在了哪里"这个问题。下面逐一介绍 Bazel 提供的六种主要采集途径。

途径一:Build Event Protocol(BEP)

BEP 是 Bazel 对外输出构建过程与结果的标准协议。Bazel 会通过 build_event_stream.proto 中定义的一系列 protobuf 消息,将构建事件流发送给你指定的后端(backend),再由后端按需聚合。

常用 BEP 输出标志

BEP 数据可以落地为本地文件或推送到远程 Build Event Service:

  • --build_event_json_file=<path>:以换行分隔的 JSON 形式写出 BEP 事件流,便于脚本直接解析;
  • --build_event_binary_file=<path>:以 length-delimited 二进制 protobuf 形式写出,体积更小;
  • --bes_backend=<endpoint>:将事件流推送到指定的 Build Event Service 后端,适合团队级的集中式指标收集。

这些标志在源码中的定义可参见 BuildEventStreamOptions.java(build_event_binary_filebuild_event_json_file)与 BuildEventServiceOptions.java(bes_backend)。

值得重点关注的 BEP 指标字段

无论聚合方式如何,BuildMetrics消息(定义于 build_event_stream.proto)中的以下子消息是通用的核心指标来源:

子消息关键字段含义
ActionSummaryactions_created/actions_executed本次构建创建/执行的总 action 数(含 aspects;actions_executed含远端缓存命中、不含本地 action 缓存命中);action_data按 mnemonic 细分每种 action 的执行数、首末执行时间、累计 CPU 时间
MemoryMetricsused_heap_size_post_buildpeak_post_gc_heap_sizeJVM 堆使用情况,仅在设置--memory_profile(强制一次 Full GC)时采集
TargetMetricstargets_configured本次构建配置的目标/aspect 数量
PackageMetricspackages_loaded成功加载的 BUILD 文件(包)数量,附package_load_metrics明细
TimingMetricscpu_time_in_mswall_time_in_msanalysis_phase_time_in_msexecution_phase_time_in_ms各阶段耗时;在 Skymeld 下分析和执行阶段交错,两阶段时间之和可能大于墙钟时间

此外,BuildFinished事件携带退出码与完成时间,TargetSummary汇总每个目标的构建/测试状态,都是做构建健康度面板的基础数据。

途径二:query / cquery / aquery 查询命令

Bazel 提供三种查询模式,分别作用于目标图(target graph)、配置目标图(configured target graph)和 action 图(action graph):

  • query:查询未配置的目标依赖关系;
  • cquery:基于具体配置查询目标及其依赖,能反映配置对图的影响;
  • aquery:查询 action 图,展示构建会产生哪些 action、每个 action 的输入输出与命令行,直接服务于性能与正确性分析。

三种模式共享同一套查询语言函数库,如deps()rdeps()somepath()等,可以组合出满足需求的复杂查询。例如用aquery查看某个目标的所有 action 及其 mnemonics,能够快速发现不必要的 action 数量或过大的 action 粒度。

途径三:JSON Trace Profile

每次构建类命令(以及query)执行时,Bazel 都会自动写出一份 JSON 格式的 trace profile,用于快速定位本次调用中时间花在了哪里。

Profile 文件的生成与配置

  • 默认写入 output base 下的command-$INVOCATION_ID.profile.gz,同时创建一个指向最近一次 profile 的符号链接command.profile.gz
  • 是否生成由--generate_json_trace_profile控制,输出位置由--profile指定,以.gz结尾的路径会启用 GZIP 压缩;
  • Bazel 默认在 output base 保留最近 5 份 profile(由--profiles_to_retain配置),显式指定--profile路径会禁用自动回收。

可视化与分析工具

  • chrome://tracing:在 Chrome 中打开chrome://tracing,点击 "Load" 选择 profile 文件即可可视化。键盘快捷键:1选择模式(查看/聚合选中事件)、2平移、3缩放、4测量两个事件间距、?查看全部快捷键。下图为一份典型的 profile 可视化结果:

Bazel JSON Trace Profile 在 chrome://tracing 中的可视化界面

  • jq:适合脚本化提取。例如提取本地 action 执行中沙箱创建步骤的所有耗时:
$ zcat $(bazel info output_base)/command.profile.gz | jq '.traceEvents | .[] | select(.name == "sandbox.createFileSystem") | .dur' 6378 7247 11850 ...

Profile 中的特殊行与常见性能问题

Profile 中除各 Bazel 线程的事件外,还包含若干特殊行:

  • action count:并发执行的 action 数,干净构建时应逼近--jobs的值;
  • CPU usage (Bazel):Bazel 每秒的 CPU 占用(值为 1 表示占满一个核);
  • Critical Path:关键路径上的每个 action 一块;
  • Main Thread:Bazel 主线程的高层事件,如 "Launch Blaze"、"runAnalysisPhase";
  • Garbage Collector:minor/major GC 停顿。

分析时应重点查找:分析阶段(runAnalysisPhase)明显慢于预期(可能是不良 rule 实现或过度展开 depset);关键路径上的单个慢 action(可考虑拆分 action 或削减传递依赖);以及只有少数线程繁忙、其余都在等待的瓶颈现象。

Profile 文件格式

顶层对象包含元数据(otherData,如 invocation ID、日期、output base)和实际跟踪数据(traceEvents)。事件中的ts/dur单位为微秒,cat取自ProfilerTask枚举。极短且相邻的事件会被自动合并,如需保留原始粒度可传--noslim_profile

途径四:Execution Log(spawn 执行日志)

当远端缓存命中率低于预期时,Execution Log 是排查机器/环境差异与 action 非确定性(non-determinism)的关键工具。它记录每个已执行 spawn 的完整信息:命令行、环境变量、输入输出文件及其 digest、执行平台与运行结果。

三种日志格式与对应标志

三个标志互斥,定义见 ExecutionOptions.java:

标志格式说明
--execution_log_binary_filelength-delimitedSpawnExecprotobuf旧格式,体积较大
--execution_log_json_file换行分隔的 JSON便于人工阅读与脚本处理
--execution_log_compact_filelength-delimitedExecLogEntryprotobuf,整文件 zstd 压缩官方推荐的格式,显著更小、生成代价更低

三者均可接受布尔值或路径字符串:传路径则写入本地文件;传true则需配合--experimental_stream_log_file_uploads将日志流式上传到远端存储。

附加 spawn 指标

在 Execution Log 基础上,可额外开启--experimental_execution_log_spawn_metrics(Bazel 5.2 起可用)。开启后日志会包含详细的 spawn 指标,本地与远端执行的 action 均适用,例如:

  • 排队时间(queuing):spawn 等待执行的时间;
  • setup / fetch / upload / network 等细分耗时:定位执行中被持续拖慢的环节;
  • 可用于对比本地与远端机器的执行性能差异。

典型用法是同一目标分别在本地与远端执行并导出两份日志,逐 spawn 对比指标,找出差异来源。

途径五:Execution Graph Log(执行图日志)

JSON Trace Profile 能给出关键路径信息,但有时还需要了解已执行 action 之间的完整依赖关系。从 Bazel 6.0 起,可以开启执行图日志:

bazel build //your:target \ --experimental_enable_execution_graph_log \ --experimental_execution_graph_log_dep_type=all

其实现位于 ExecutionGraphModule.java,输出为一个 zstd 压缩、length-delimited 的execution_graph.Nodeprotobuf 文件(默认execution_graph_dump.proto.zst)。相关标志包括:

  • experimental_enable_execution_graph_log:开启执行图日志;
  • experimental_execution_graph_log_dep_type:依赖信息粒度,none/runfiles/allall报告每一条 action 间边;
  • experimental_execution_graph_log_path:指定本地输出路径,支持绝对路径、相对路径或%workspace%前缀(设置该路径且未开启主开关会直接报错);
  • experimental_execution_graph_log_queue_size/execution_graph_log_queued_bytes_limit:写盘队列大小与字节上限,用于在峰值内存与写盘阻塞之间权衡(-1 表示无界)。

日志中的每个节点记录Metrics(开始时间戳、总耗时、fetch/discover_inputs/parse/process/queue/retry/setup/upload/network等细分耗时)、mnemonic、所属 rule class 与 target label,并通过dependent_index记录依赖边。从源码看,节点还特别处理了 retry(retryOf指向先前尝试)与 action 缓存命中情况(ExecutionGraphModule.java 中CachedActionEvent的注释说明:关键路径中间存在缓存命中的 action 时,必须记录它们才能正确重建依赖树)。

该日志的核心价值在于计算drag——某个节点对关键路径施加的"拖累",即从执行图中移除该节点理论上能节省的时间。基于此,可以在真正改动之前预测对构建图与 action 图的影响。

途径六:使用 bazel-bench 做基准测试

除单次构建的指标采集外,跨版本、跨提交的性能回归检测需要可重复的基准测试。bazel-bench 是面向 Git 项目的基准测试工具,支持两类场景:

  • Project benchmark:在同一个 Bazel 版本下对比两个 git commit 的构建性能,用于检测构建自身的回归(通常源于新增依赖);
  • Bazel benchmark:在同一个 git commit 下对比两个 Bazel 版本,用于检测 Bazel 本身的回归(适合 Bazel 维护者或 fork 场景)。

它监控的指标包括墙钟时间(wall time)、CPU 时间、系统时间以及 Bazel 的 retained heap 大小。官方建议在专用的物理机上运行 bazel-bench,避免其他进程干扰,以降低结果方差。

六种途径的选型建议

需求推荐途径
建立构建健康度基线、做团队级趋势面板BEP(BuildMetrics各字段)
定位某次构建内部的耗时分布JSON Trace Profile(command.profile.gz+ chrome://tracing)
排查远端缓存命中率低、action 非确定性Execution Log(--execution_log_compact_file+--experimental_execution_log_spawn_metrics
评估改动 action 图的影响、计算关键路径 dragExecution Graph Log(--experimental_enable_execution_graph_log
检查目标/action 图结构本身query / cquery / aquery
跨提交或跨版本做性能回归对比bazel-bench

更完整的"指标 → 诊断 → 修复"闭环流程,可进一步阅读构建性能拆解相关章节,以及仓库中同目录下的迭代速度优化、内存优化等配套文档。掌握上述采集手段,你就拥有了把"感觉慢"转化为"数据慢"的能力,接下来的一切优化都将有据可依。

【免费下载链接】bazela fast, scalable, multi-language and extensible build system项目地址: https://gitcode.com/GitHub_Trending/ba/bazel

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询