生产测量与 CI 性能回归检测
从开发机到生产
ch01-01 到 ch01-04 讲的是开发机上做 microbenchmark A/B 比较:消除噪声、报中位数、做假设检验。那套规矩回答的是「我这个改动方向对不对」。但还有两个问题它答不了:
- 上线后用户到底快了没有? microbenchmark 的结论不能直接推给生产(ch01-01 反复强调),你得在生产环境里测。
- 怎么防止性能随着版本累积悄悄退化? 大项目变更飞快,性能回归 bug 会以惊人的速度漏进生产代码,光靠人每次盯,迟早漏。
这一篇就回答这两个:生产测量怎么做、CI 里怎么自动检测性能回归。素材主要来自 Bakhvalov《Performance Analysis and Tuning on Modern CPUs》§2.2 和 §2.3。
生产测量:接受噪声,用统计
生产环境和开发机最大的差别是:你不能消除噪声,也不该试。ch01-03 那套「锁频、绑核、关 Turbo」的活,在生产里全是毒药:你消除了噪声,测的就不是用户会经历的东西。生产测量的原则正好反过来:复刻真实 + 用统计方法处理噪声。
几个要点:
- 共享基础设施的干扰。公有云上,你的服务跑在和别人共享的同一台物理机上(虚拟化 / 容器),邻居进程会以不可预测的方式影响你的性能。你在开发机上复刻不出这种干扰。
- telemetry:在真实用户设备上采样。大厂越来越流行在用户端埋点采集性能数据。Bakhvalov 举了 Netflix 的 Icarus telemetry 服务,它跑在散布全球的几千台设备上,帮工程师看到「真实用户怎么感知性能」,这种数据在实验室里复刻不出来。
- 开销必须极低。生产 profiling 的原则是「极低开销是首要」。Ren 等人 2010 年那篇论文的原话大意:在跑真实流量的数据中心机器上做持续 profiling,可接受的总开销在 1% 以下。所以只能用 lightweight 方法(低频采样、只抽一部分机器、短时间窗)。
- 统计方法处理分位数指标。生产性能看的不是「平均多快」,而是「p90 / p99 延迟多少」,长尾才是用户体验的杀手。LinkedIn 的做法(Bakhvalov 引 Liu 等人 2019)是用统计方法在生产环境做 A/B 测试,比较这些分位数指标。
一句话:生产测量 = 保留噪声 + 统计推断,和 micro 的「消除噪声 + 干净对比」是两套语言。
CI 性能回归检测:为什么简单阈值不灵
产品迭代飞快,性能回归会持续漏进来。靠什么挡?
第一反应:人眼看图。 别。人会迅速失去焦点,尤其在噪声大的图上。Bakhvalov §2.3 那张图里,8 月 5 号的那次回落人眼能抓到,但后续几个小回归大概率被漏。而且这是每天都要做的、无聊的活,不适合人。
第二反应:定一个阈值,「跌幅超过 X% 就报警」。 听起来合理,实际两个硬伤:
- 阈值极难选。设低了,一堆纯噪声的小波动触发报警,你天天查空气;设高了,真回归被滤掉。而且小回归会累积:Bakhvalov 举的例子,阈值设 2%,两次各 1.5% 的回归都被滤掉,两天累积成 3%,已经超阈值却没人管。
- 每个 test 要单独调阈值。不同 benchmark 的噪声水平不同,一个阈值不可能通用。Chromium 项目的 LUCI 就是每个 test 显式配阈值的例子,能跑,但维护成本高。
变点检测(change point analysis)
更新的做法是变点检测:不盯单一阈值,而是盯整条时间序列的分布什么时候变了。MongoDB 团队(Daly 等人 2020)在他们的 CI 系统 Evergreen 里实现了一套,用一个叫「E-Divisive means」的算法,在时间序列里自动找「分布发生变化的点」,在图上标出来,自动开 Jira ticket。这种做法的好处是它对噪声鲁棒(找的是分布结构变化,不是单次抖动),而且不需要每个 test 手动调阈值。
另一个思路(Bakhvalov 引 Alam 等人 2019 的 AutoPerf):用硬件性能计数器(PMC,见 ch02/ch03)给每个函数建一个「性能指纹」,改动后的版本如果指纹偏离了基线,就标记异常。这种法子能抓到一些藏在并行程序里的复杂性能 bug。
一个 CI 性能系统该自动化的事
不管底层用阈值还是变点检测,Bakhvalov §2.3 给了一个典型 CI 性能系统该自动化的五步,很实在:
- 搭建被测系统(Setup system under test)
- 跑负载(Run workload)
- 报结果(Report)
- 判断性能有没有变(Decide if performance has changed)
- 可视化(Visualize)
外加几条要求:支持自动和人工两种提交方式、结果可重复、发现回归要及时开 ticket。趁代码还热、作者还没切到下一个任务,回归最容易被修;拖两周,作者都忘了改过啥,修起来事倍功半。
把这一章串起来
到这里,ch01 的闭环就齐了:
- ch01-01~04:microbenchmark 在开发机上做 A/B,消除噪声、报中位数、做假设检验。回答「改动方向对不对」。
- ch01-05(本文):生产测量 + CI 回归。生产复刻真实噪声 + 统计分位数;CI 用变点检测自动抓回归。回答「上线后真的快了吗、有没有悄悄退化」。
两套不能混:别拿 micro 的数字给生产背书,也别在生产环境里用 micro 的「消除噪声」那套。它们是测量光谱的两端,中间的过渡靠 macro benchmark(代表真实负载但受控),那是更后面章节的事。
参考资源
- Bakhvalov, D. 《Performance Analysis and Tuning on Modern CPUs》§2.2 Measuring Performance In Production、§2.3 Automated Detection of Performance Regressions(Netflix Icarus、Ren 2010、Liu 2019、MongoDB Evergreen / Daly 2020、AutoPerf / Alam 2019 均出于此)
- Chromium LUCI 性能 dashboard 文档
- 本卷 ch01-01(micro vs macro 边界)、ch01-04(统计方法)