什么是持续基准测试?
持续基准测试是一种软件开发实践,团队成员频繁地对他们的工作进行基准测试,通常每人至少每天进行一次——从而每天会产生多次基准测试。每一次基准测试都由自动化构建进行验证,以尽可能快地检测出性能退化。许多团队发现这种方法能够显著减少性能退化,并让团队能够更快速地开发出高性能的软件。
到目前为止,软件行业中的每个人都知道持续集成 (CI)。从根本上说,CI 的目的是在软件功能退化进入生产环境之前对其进行检测和预防。同样,持续基准测试 (CB) 的目的是在软件的 性能 退化进入生产环境之前对其进行检测和预防。出于与每次代码变更都要在 CI 中运行单元测试相同的理由,每次代码变更都应当在 CB 中运行性能测试。事实上,这个类比如此贴切,以至于本节的第一段不过是马丁·福勒 2006 年关于持续集成的引言的填空改写版本。
🐰 性能缺陷也是缺陷!
在 CI 中进行基准测试
误区:你无法在 CI 中运行基准测试
大多数基准测试工具使用系统挂钟时间来测量延迟或吞吐量。这非常有用,因为这些正是我们开发者最关心的指标。然而,通用的 CI 环境在测量挂钟时间时往往噪声较大且不稳定。在进行持续基准测试时,这种波动会给结果带来不必要的噪声。
有几种方式可以处理这个问题:
在几乎所有情况下,裸金属运行器都是目前为止最好的选择。Bencher 提供的裸金属运行器的方差不到 2%。相比之下,GitHub Action 运行器在不同运行之间的方差可能超过 30%。降低持续基准测试环境中的波动与噪声,能让你检测到更加细微的性能退化。
性能很重要
误区:你无法察觉到 100 毫秒的延迟
经常能听到有人声称人类无法感知 100 毫秒的延迟。人们常常援引一篇 Nielsen Group 关于响应时间的文章来支持这一说法。
0.1 秒大约是让用户感觉系统即时响应的极限,这意味着除了显示结果之外不需要任何特别的反馈。
- Jakob Nielsen,1993 年 1 月 1 日
但事实并非如此。在某些任务中,人类可以察觉到低至 2 毫秒的延迟。一个简单的证明方法就是 Dan Luu 的一个实验:打开终端,先运行 sleep 0; echo "ping",然后再运行 sleep 0.1; echo "pong"。你注意到差别了吧‽
另一个常见的混淆点是延迟的感知与人类反应时间之间的区别。尽管人对视觉刺激做出反应大约需要 200 毫秒,但这与对事件本身的感知是两码事。打个比方,你可以察觉到你的火车晚点了两分钟(感知到的延迟),尽管整趟火车行程要两个小时(反应时间)。
性能很重要!性能本身就是一个特性!
- 每快 100 毫秒 → 转化率提升 1%(Mobify,每年收入增加 +$380,000)
- 快 50% → 销售额提升 12%(AutoAnything)
- 快 20% → 转化率提升 10%(Furniture Village)
- 快 40% → 注册量提升 15%(Pinterest)
- 快 850 毫秒 → 转化率提升 7%(COOK)
- 每慢 1 秒 → 用户减少 10%(BBC)
随着摩尔定律的终结,可以并行运行的工作负载将需要被并行化。然而,大多数工作负载仍需要串行运行,而仅仅靠堆砌更多的算力来解决问题正迅速变成一种棘手且昂贵的方案。
面对这样的变化,持续基准测试是开发和维护高性能现代软件的关键一环。

持续基准测试工具
在创建 Bencher 之前,我们曾试图寻找一款工具,它能够:
- 在本地和 CI 中使用 完全相同 的裸金属硬件执行基准测试
- 跨多种语言跟踪基准测试
- 无缝地采集语言标准基准测试工具的输出
- 可扩展以支持自定义基准测试工具的输出
- 开源且支持自托管
- 能与多家 CI 平台协同工作
- 具备用户身份认证和授权
不幸的是,并不存在满足所有这些条件的工具。要查看我们从中获取灵感的现有基准测试工具的完整列表,请参见先前技术。
CI 之外的持续基准测试
CI 的定位应当是最后一道检查,而不是唯一进行测试的地方。Bencher 是第一款允许你在本地和 CI 中使用 完全相同 的裸金属硬件来运行基准测试的持续基准测试工具。这让开发者和智能体能够将他们本地的工作进度与项目性能历史上的任意一点进行对比。
在本地硬件上运行时,Bencher 裸金属模式允许你继续并行处理其他任务。你不必停下系统上的其他所有工作、切出旧分支、再去跑一次对比。
在云端环境中运行时,Bencher 裸金属模式让你可以信任测试结果。你无需担心吵闹的邻居、资源节流,或是运行过程中被中途换到另一台宿主机上。
大型科技公司的持续基准测试
在 Microsoft、Facebook(现 Meta)、Apple、Amazon、Netflix 和 Google 等无数大型公司内部,都已经开发出类似 Bencher 的工具。作为行业巨头,他们深知在开发过程中监控性能的重要性,并通过持续基准测试将这些洞察融入到开发流程中。我们打造 Bencher,正是为了把持续基准测试从大型科技公司的围墙之内带到开源社区。若要查看与大型科技公司持续基准测试相关的文章链接,请参见先前技术。
Bencher: 持续性能基准测试
Bencher是一套持续基准测试工具。 你是否曾经因为性能回归影响到了你的用户? Bencher可以防止这种情况的发生。 Bencher让你有能力在性能回归被合并 之前 就进行检测和预防。
- 运行: 使用 完全相同 的裸机运行器和你喜爱的基准测试工具,在本地或CI中执行你的基准测试。
bencherCLI在裸机上编排你的基准测试运行并存储其结果。 - 追踪: 追踪你的基准测试结果的趋势。根据源分支、测试床和度量,使用Bencher web控制台来监视、查询和绘制结果图表。
- 捕获: 使用 完全相同 的裸机硬件,在本地或CI中捕获性能回归。Bencher使用最先进的、可定制的分析技术在它们被合并之前就检测到性能回归。
基于防止功能回归的原因运行单元测试,我们也应该使用Bencher运行基准测试以防止性能回归。性能问题就是错误!
开始捕捉性能回归 — 免费试用Bencher Cloud。
持续基准测试与本地基准对比
已经有一些基准测试工具可以让你在本地对比结果。在进行性能调优时,本地对比对于快速迭代非常有帮助。然而,它不应被用作持续捕捉性能退化的依赖手段。正如能够在本地运行单元测试并不能免除 CI 的必要性一样,能够在本地运行和对比基准测试也不能免除持续基准测试的必要性。
Bencher 提供了一些本地基准对比工具所不具备的特性:
- 在不同测试平台之间对比相同的基准测试
- 跨语言和跨工具的基准测试对比
- 基准测试结果的协作与共享
- 在专用测试平台上运行基准测试以降低噪声
- 不再需要复制粘贴
持续基准测试与应用性能管理 (APM)
应用性能管理 (APM) 是现代软件服务不可或缺的工具。然而,APM 的设计初衷是在生产环境中使用。等到性能退化被检测到时,它已经在影响你的客户了。
大多数缺陷最终的代价都高于预防它们所需的成本。缺陷出现时是昂贵的,既包括修复缺陷的直接成本,也包括由于关系受损、业务流失和开发时间浪费所带来的间接成本。
— Kent Beck,Extreme Programming Explained
Bencher 提供了一些 APM 工具所不具备的特性:
- 在性能退化被合并 之前 就将其捕获
- 将性能变化及其影响纳入代码评审
- 在生产环境中没有额外开销
- 对本地部署同样有效
- 无需修改生产环境源代码
持续基准测试与可观察性
玫瑰即使换了别的名字,也依然芬芳如故。请参见上文的持续基准测试与应用性能管理 (APM)。
持续基准测试与持续集成 (CI)
持续基准测试 (CB) 是对持续集成 (CI) 的有力补充。出于与每次代码变更都要在 CI 中运行单元测试相同的理由,每次代码变更都应当在 CB 中运行性能测试。
尽管单元测试和验收测试作为标准开发实践已被广泛接受,但这种趋势并未延伸到性能测试的领域。目前,主流的工具让测试人员倾向于编写一次性的代码,并形成点一下、写个脚本的心态。把性能测试当作一等公民来对待,可以创建出覆盖更多功能的更好的测试,进而催生出更好的工具来创建和运行性能测试,最终形成一套可维护、并且自身也可以被测试的测试套件。
持续基准测试与持续负载测试
为了理解持续基准测试和持续负载测试之间的区别,你需要先理解基准测试与负载测试之间的区别。
| 测试类型 | 测试范围 | 测试用户 |
|---|---|---|
| 基准测试 | 函数 - 服务 | 一个 - 多个 |
| 负载测试 | 服务 | 多个 |
基准测试可以测试软件从函数级别(微基准)一直到服务级别(宏基准)的性能。基准测试非常适合以隔离的方式测试代码中某个特定部分的性能。负载测试则只在服务级别测试软件的性能,并会模拟多个并发用户。负载测试非常适合在特定负载下测试整个服务的性能表现。
🍦 想象一下,我们想要跟踪一辆冰淇淋车的性能。可以用基准测试来测量挖一勺冰淇淋需要多长时间(微基准),也可以用基准测试来测量一名顾客从下单、拿到冰淇淋到完成支付一共需要多长时间(宏基准)。而负载测试则可以用来观察冰淇淋车在炎热的夏日里服务 100 名顾客的表现。