2235 字
约 7 分钟
18
S27-A24 · 实测的方差:两次数差 20%,是"回归"还是"噪声"

一次"换连接池"之后,压测 P99 从 120ms 变成 145ms,团队立刻回滚。回滚后复测又是 118ms——于是"结论"变成"连接池确实更慢"。而三个月后另一组人测出"新池更快 5%"。

这类"结论反复"的根源是"方差":同一个配置连测三次,P99 可能差 15–25%——这个"恰好"是这类误判的结构原因:性能优化的收益常在 10–20%,而噪声也在 10–20%——于是"同一组数据"能被读出两种结论。
而"20% 的差异"恰好落在"噪声"里。

1. 方差从哪来

来源 典型量级
GC 时机 10–30%
缓存预热状态 20–50%(首轮)
机器负载(其它进程) 5–20%
网络抖动(跨机) 5–15%

第二项最大:**首轮"全是冷缓存"**第二项的"量级"很夸张:首轮 P99 可能是稳态的 1.5–2 倍——于是"新配置的第一次测试"几乎总是"看起来更慢"。
,于是"第一轮永远慢"——而它常被当成"新配置更慢"。

2. 三种"控制方差"的手段

手段 效果
固定轮次 + 丢弃首轮 去掉"冷启动"
交替执行(A/B/A/B) 摊平"时间漂移"
同机同时段 去掉"环境差异"

第二项的"实现"要"成对":A1 B1 A2 B2 … A10 B10——而不是"先测完 A 十次再测 B 十次"(那样"时间漂移"全落在 B 上)。
第二项是"最便宜"的:不需要更多机器,只改执行顺序——于是"温度、负载、外部流量"被平均到两组。

"该测几次"可以被"两行公式"估出来,而不必"凭感觉加轮次":

// 粗估"需要的轮次"(不追求统计严格,只求量级对)
观察到的差异 ≈ 20%,同配置波动 ≈ 20%
=> 差异/波动 = 1 → 需要"十几轮"才可能分辨
=> 若差异 40%、波动 20% → "3–5 轮"即可
// 判据:先量"波动",再决定"轮次"——反过来会白测

3. "20% 差异"该不该信

次数 判断
各 1 次 不可信
各 3 次取中位 弱(若重叠则不成立)
各 10 次 + 看区间 可信(若区间不重叠)

第三项是"正解":**比"两个中位数"更有信息的是"两个区间"**第三项的"实操"是"取 min–max 或 P25–P75":区间"不重叠"才叫"差异","重叠"就叫"同一水平"——而它能把"反复回滚"直接掐掉。
——区间重叠 → 差异不可分辨;不重叠 → 可信。

// 两种"结论"的写法(同一组数据)
差:A 的 P99 是 120ms,B 是 145ms,B 慢 20%
好:A 的 P99 区间 [112, 128],B 的 [136, 158],区间不重叠 → 差异可信
// 判据:区间重叠时,正确结论是"未观察到差异"

4. "首轮慢"的三种来源

来源 说明
JIT 未编译 前几千次调用慢
缓存空 每次都要回源
连接池未建 每次新建连接

这个"不知道"的代价很具体:"两次测试都含首轮"时,两个数都偏高,于是"差异被压缩"——你以为在比较,其实在比"谁的冷启动更快"。
三者叠加会让"首轮"比"稳态"慢一倍以上——而"丢弃首轮"这条规则,很多人不知道。

"区间"可以用"一个脚本"算出来,而它比"Excel 手算"可靠:

// 十轮结果 → 中位数与区间(用 sort + awk,或无工具也能做)
for i in $(seq 10); do ./bench.sh | awk '{print $1}' >> a.txt; done
sort -n a.txt | awk '{v[NR]=$1} END{printf "p50=%.0f min=%.0f max=%.0f\n", v[int(NR*0.5)], v[1], v[NR]}'
// 判据:A/B 两组"min–max"不重叠 → 差异可信;重叠 → 未观察到差异

5. "方差"怎么量

方法 说明
同配置连测 5–10 次 看波动范围
分位数看 P50 与 P99 P99 方差通常更大
用"变异系数"(std/mean) 便于横向比较

第一项是"最便宜的":同一配置跑 10 次,看"最大值/最小值"的比值第一项的"判据"可以写成"同一配置自身波动 ≤ 5% 才谈对比"——超过 5% 时,先修"环境"(隔离负载、固定 CPU、关掉其它 agent)。
——比值 1.3 以内算稳,1.5 以上说明"环境不稳"。

"环境不稳"要先"证明",否则"加轮次"只是"把噪声平均掉":

// 三步"环境体检"(同配置跑 10 次)
1 变异系数 = std/mean:> 0.15 → 环境不稳
2 最大值/最小值:> 1.5 → 有长尾干扰(GC/其它进程)
3 分时看:把 10 次按"时间顺序"排,若"越来越慢" → 有累积效应(内存/日志)
// 判据:三步都正常,才值得"增加轮次";否则先修环境

6. 排错:五种"方差问题"

现象 成因 处理方向
两次结论相反 次数太少 加轮次 + 交替
首轮总慢 冷启动 丢弃首轮
同一配置波动大 环境不稳 固定环境
P99 忽高忽低 长尾敏感 看区间而非单点
跨机器不同 机器差异 同机对比

"P99 的方差"比"P50"大得多,所以"看哪个指标"决定"要测几次":

// 三个指标的方差(同一组数据,经验值)
P50:波动 5–10%   → 3 次可看趋势
P99:波动 15–30%  → 10 次才敢下结论
错误率:波动 2–5 倍(0.1% 量级)→ 需要"更多请求"而不是"更多轮次"
// 判据:越靠"尾部"的指标,越需要"样本量"

7. 边界:什么情况"一次就够"

场景 为什么 做法
差异极大(> 2 倍) 噪声盖不住 1–2 次
只判断"方向" 不需精确 1 次
已有"历史基线"可比 减少变量 1 次
功能验证(非性能) 与方差无关 1 次

最后一条与"决策"有关的边界:"差异落在噪声里"也是一种结论——它的意思是"性能不是这次决策的变量",于是"该看可维护性、生态、团队熟悉度"——而不是"再测一次找差异"。
判断口径一句话:"这次的差异,会不会被'同配置自身的波动'盖住"。会(差异 < 20%),必须"多次 + 区间";不会(差异 2 倍以上),一次就够。

还有一个"团队"现实:"回滚"的成本远低于"多测几轮"——于是"看到 20% 变慢就回滚"成了合理的短期选择。而它的长期代价是"没人知道到底哪个更快":回滚多了,团队会形成"性能不可测"的共识,之后所有性能优化都会被质疑。这是"方差"最贵的地方,而它不在任何指标里。

所以"测准"这件事的收益,往往不在"这一次的结论",而在"下一次还愿意测"。

再补一个"跨团队"的坑:两拨人用"同一份脚本"测出不同结果,往往不是"脚本问题",而是"客户端机器不同"——压测端自己的 CPU/GC 会污染"响应时间"(因为计时在客户端)。所以"跨团队对比"的第一件事,是"确认两边的压测端规格一致",而不是"核对脚本参数"。这一条能解释掉大量"我们测出来完全不一样"的争论。

而这个坑还有个"反向"版本:客户端弱、服务端强时,"压不上去"会被读成"服务端到顶了"——于是"扩容"变成了"无效动作",因为"上限在客户端"。判据很简单:看压测端的 CPU 是否打满——打满就说明"测的是客户端"。

而"看客户端一眼"只要十秒钟。

这条经验值十秒钟,却能省掉一轮"扩容后仍无改善"的困惑。

8. 自测

  1. 方差的四个来源里,哪一个最大?为什么"首轮永远慢"?
  2. "交替执行"解决了什么?它为什么"最便宜"?
  3. "两个区间"为什么比"两个中位数"信息更多?区间重叠时正确结论是什么?
  4. "首轮慢"的三个来源是什么?"丢弃首轮"这条规则为什么常被忽略?
  5. "变异系数"有什么用?"最大值/最小值 1.5"说明什么?
  6. 判断"一次是否够"的那个问题是什么?
S27-A24 · 实测的方差:两次数差 20%,是"回归"还是"噪声"
https://www.clxhxhhr.top/posts/6661/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。