Envoy 编译耗时根因剖析与高效构建环境搭建指南
发布时间:2026/9/14 18:26:25 作者:尧图编辑部 阅读量:1,286

Envoy 编译耗时根因剖析与高效构建环境搭建指南【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy导读Envoy 作为云原生高性能边缘/中间/服务代理其 C 源码体量与扩展数量极其庞大全量编译动辄需要数十分钟甚至更久这也是官方 FAQ 中被反复提及的经典问题。本文以官方 FAQ 文档 docs/root/faq/build/speed.rst 为骨架结合当前仓库的 Bazel 构建系统与预编译头PCH实现从为什么慢的三大根因出发给出从云上开发机选型到构建参数调优的完整实战方案帮助贡献者与使用者显著缩短 Envoy 的构建与测试周期。问题背景来自官方 FAQ 的灵魂拷问在 Envoy 官方文档的 FAQ 总览 中Build 分类下与binaries二进制产物、boringsslTLS 库并列的一篇就是这篇专门回答Why does Envoy take so long to compile?为什么 Envoy 编译这么慢的 speed.rst。官方 FAQ 的态度非常坦诚Envoy 编译的算力消耗强度之高是计算密集computationally intensive级别的并且官方承认编译速度问题客观上**阻碍了偶发贡献者occasional contribution**的参与积极性。要解决这个问题并没有一蹴而就的简单答案但它详细列出了造成这一局面的三大根因并给出了当前阶段最有效的缓解策略——这也正是本文要展开的两条主线。根因一C 与模板是编译速度的天敌官方文档指出的第一个根因是语言层面的C code, and especially C code with templates, has slow compilation speed.C尤其是大量使用模板templates的 C 代码编译速度天然缓慢。这并非 Envoy 独有而是 C 语言本身的固有特性模板在实例化instantiation阶段需要编译器为每个模板参数组合生成完整代码头文件中的模板定义几乎每个翻译单元translation unit都要重新解析一遍。从当前仓库的源码规模可以直观感受到这种压力source/common 目录下共有 1070 个文件其中 575 个.h头文件与 420 个.cc源文件source/extensions 目录下更是达到 2512 个文件包含 1085 个.h与 931 个.cc。这些头文件通过#include层层传递envoy/common/、envoy/http/等公共头文件被几乎所有源文件引用编译器需要反复处理海量的声明与模板定义IO 与 CPU 开销都被成倍放大。根因二gmock 测试框架加剧了模板编译负担第二个根因与 Envoy 的测试基础设施直接相关Envoy tests make use of gmock, which is heavily templatized, and thus has very poor compilation speed.Envoy 的测试大量依赖 gmockGoogle Mock。gmock 本身是重度模板化的框架——每个 mock 类的每个方法都要通过模板机制生成匹配器matcher、动作action与期望校验逻辑这导致测试代码的编译速度比普通生产代码还要差得多。仓库中的证据非常充分仅 test/mocks 目录就包含 250 个文件其中 116 个.h、98 个.cc覆盖了upstream、http、network、ssl等几乎全部核心抽象接口的 mock 实现。此外 test/extensions2286 个文件与 test/common1671 个文件中的测试大量引用这些模板化 mock。因此跑一次完整测试构建等价于对数十万个模板实例进行编译其耗时往往超过生产代码本身。根因三代码与扩展规模持续快速增长第三个根因是规模效应的叠加The amount of Envoy code, and the number of extensions, continues to grow rapidly, compounding the problem.Envoy 的代码量与扩展数量持续快速膨胀进一步放大了前两个问题。当前仓库的扩展注册表 source/extensions/extensions_build_config.bzl 中以envoy.xxx.xxx: //source/extensions/...形式登记的扩展条目多达339 个涵盖访问日志、集群、压缩、过滤器、负载均衡、密钥提供者、统计接收器等十几类。默认构建会一次性编译这些扩展中的绝大部分正如 FAQ 所说规模问题与模板问题相互叠加compounding让总编译时间雪上加霜。官方推荐的缓解策略为 Envoy 开发配备重火力机器FAQ 明确指出在更好的解决方案如将部分扩展拆出主仓库、预编译头等落地之前最有效的做法是为 Envoy 开发使用一台性能强大的机器。具体建议如下方案一云虚拟机 VS Code Remote DevelopmentUse a cloud based VM along with a tool like vscode remote development.官方推荐使用云端虚拟机并搭配 VS Code 的 Remote Development 能力进行远程开发。这样可以将昂贵的编译算力放在云端本地仅作为轻量编辑器入口ssh/remote插件会透明地完成本地编辑与远程编译环境的衔接。方案二CPU 与内存配比基准36 核每核约 2 GiB 内存Having 36 cores and around 2 GiB of RAM per core still allows the entire source tree to built and tested in a reasonable amount of time.官方给出的经验配比是36 核以上 CPU每核配约 2 GiB 内存即 36 核约 72 GiB 内存。在此配置下整个源码树的全量构建与测试仍能在合理时间内完成。这一建议与当前仓库的 Bazel 配置是自洽的仓库根目录的 .bazelrc 第 36 行设置了build --jobsHOST_CPUS-1即 Bazel 会默认按宿主机 CPU 数减 1的数量并行执行编译任务。这意味着更多核心数会直接线性转化为更高的并行编译吞吐正是36 核建议的底层依据。同时 C 编译单任务内存峰值较高每核 2 GiB 的配比可有效避免并行任务因内存不足而 swap 拖慢整体构建。方案三构建目录必须放在本地临时 SSD 而非远程块存储If using a cloud machine, make sure to build on a local ephemeral SSD versus a remote block store such as EBS. The build involves a large amount of disk access and will become disk bound if not using a fast local drive.这是 FAQ 中最容易被忽略但实战价值极高的一条构建过程涉及海量磁盘访问如果不用快速本地盘构建会沦为磁盘瓶颈disk bound。Envoy 的全量构建会产生数十万次文件读写编译产物、Bazel 的 sandbox、缓存、符号表等因此首选云机的本地临时 SSDlocal ephemeral SSD如 AWS 的instance store避免将 Bazel 工作区放在如 EBS 这类**远程块存储remote block store**上其网络 IO 延迟会成为整个构建的瓶颈。从 Bazel 的角度磁盘性能同样影响着缓存命中后的验证与拷贝开销。仓库 .bazelrc 还提供了本地缓存接入示例build:cache-local --remote_cachegrpc://localhost:9092配合高速本地 SSD 可进一步摊薄重复构建成本。仓库现状FAQ 中设想的未来方案已部分落地FAQ 在阐述缓解方案时提到长期解决方向包括将部分扩展拆出主仓库、预编译头precompiled headers等。值得注意的是预编译头方案在当前仓库中已经以 Clang PCH 的形式落地可作为理解构建优化的最佳源码案例。Clang PCH 的开关与 Bazel 配置PCH 功能通过 Bazel 的config_setting与命令行 define 联合控制bazel/BUILD 中定义了clang_pch_build这个config_setting匹配条件为--defineENVOY_CLANG_PCH1.bazelrc 中提供了现成的配置组# Flags for Clang PCH build:clang-pch --spawn_strategylocal build:clang-pch --defineENVOY_CLANG_PCH1即构建时追加--configclang-pch即可开启。注意它同时强制--spawn_strategylocal因为 PCH 文件的生成与使用强依赖本地文件系统且 envoy_pch.bzl 中给 PCH 目标打上了tags [no-remote]明确禁止远程执行。PCH 的底层实现原理从 bazel/pch.bzl 的规则实现可以看到完整的 PCH 生成流程汇总头文件将includes属性中列出的头文件逐个生成#include ...语句写入一个xxx.h聚合头文件见pch.bzl第 32-36 行编译为 PCH 产物以-x c-header -Xclang -fno-pch-timestamp参数调用 clang 编译器把聚合头编译为xxx.pch文件第 38-39 行其中-fno-pch-timestamp用于屏蔽时间戳干扰保证 PCH 可被缓存复用编译器强校验第 29-30 行会检查工具链是否包含clang非 clang 编译器直接fail()报错避免误用消费侧接入封装宏envoy_pch_library见 bazel/envoy_pch.bzl通过-include-pch $(location ...)将 PCH 注入到目标库的编译参数中第 18-25 行并在clang_pch_build命中时把 PCH 依赖挂接到对应目标。仓库中已实际采用该机制的例子可见 source/common/common/BUILD 中对envoy_pch_library的调用——核心公共头文件被预编译成 PCH 后大量依赖它的源文件可以跳过重复解析显著降低平均编译耗时。这也验证了 FAQ 中没有简单答案、需要长期工程投入的判断Envoy 选择了用构建系统层面的机制来对冲语言与规模层面的开销。扩展裁剪从源头减少编译规模针对扩展数量持续增长这一根因仓库同样提供了裁剪手段。source/extensions/extensions_build_config.bzl 是扩展注册的中心文件由 bazel/envoy_build_config.bzl 中的default_envoy_build_config仓库规则符号链接为默认构建配置。开发者可以基于它生成精简版扩展配置只保留自己需要的扩展如仅envoy.clusters.static、envoy.filters.http.router等从而大幅缩小参与编译的目标集合——这正是 FAQ 所展望的将部分扩展拆出主仓库的轻量级本地替代方案。总结如何搭建一个高效的 Envoy 开发环境综合官方 FAQ 与仓库构建系统现状搭建高效 Envoy 开发环境的最优实践可以归纳为四条算力先行使用 36 核、每核约 2 GiB 内存的云 VM配合 VS Code Remote Development 做远程开发让--jobsHOST_CPUS-1的并行策略充分发挥多核优势磁盘就位确保 Bazel 输出目录位于本地临时 SSD 上切勿放在 EBS 等远程块存储避免构建退化为磁盘瓶颈善用 PCH使用 clang 工具链时追加--configclang-pch开启预编译头让高频公共头文件只解析一次按需裁剪通过精简 source/extensions/extensions_build_config.bzl 中的扩展注册表只编译实际需要的扩展从源头控制编译规模。理解为什么慢是解决如何变快的第一步。官方 FAQ 给出的方向——强算力、快磁盘、以及长期工程手段PCH、扩展拆分——在当前仓库中均已得到验证与落地按上述清单配置即可将 Envoy 的全量构建与测试控制在可接受的周期内让贡献者把精力放在代码本身而非等待编译上。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考