一次"换 JSON 库"的实测显示"快了 32%",于是另一个团队也在自己的服务里换——只快了 4%。因为那次实测的负载"90% 是序列化",而另一个服务"序列化只占 8%"。
实测结论的"可迁移性"不取决于"数字多漂亮",而取决于"这个数字是"在什么占比下"得到的"。这个"缺"有心理原因:写报告的人关心"我优化了多少",而读者关心"我能拿多少"——两个问题之间差一个"占比"的乘法。
而报告里最常缺的,恰恰是"占比"。
1. 结论迁移的三道关
| 关 | 判据 |
|---|---|
| 机制关(为什么快) | 能说出"省了哪一步" |
| 占比关(这部分占多少) | 该环节在总耗时里的比例 |
| 环境关(依赖什么条件) | 缓存/数据/并发是否相同 |
第二关是最常被漏的:**"快 32%"在"该环节占 90%"的负载下成立;在"占 8%"的负载下只有 2.6%**这个乘法很冷酷:32% 的提速在"占 8%"的负载上只有 2.6%——而那"2.6%"还包含"迁移与回归测试"的成本,于是"净收益为负"。
——而它"不值得换库"。
2. "机制关"的三种深度
| 深度 | 例子 |
|---|---|
| 现象 | "换了库就快了" |
| 环节 | "少了两次字符串拷贝" |
| 机制 | "避免了"先解码再编码"的二次遍历" |
第三项的"写法要求"是"避免用形容词":"更快/更省"不能迁移,"少了一次 map 的遍历"能——后者可以被"对照到别人的代码里"。
第三项才"可迁移":因为它能判断"你的场景里是否也有这两次遍历"。
3. "占比关"怎么量
| 方法 | 说明 |
|---|---|
| Profiler 火焰图 | 直接看该方法占比 |
| 分别计时(打点) | 粗但快 |
| 对照组(开关新旧实现) | 最准 |
第一项是"最直接"的:"这个方法占总 CPU 的百分比"就是占比第一项的"注意"是"采样偏差":火焰图按"CPU 时间"采样,于是"IO 等待"不会出现——而"IO 占比高"的服务里,CPU 占比会"虚高"。
——而"占比 × 提速幅度"才是"你能拿到的收益"。
// 期望收益的估算(迁移前就能算)
期望收益 ≈ 该环节占比 × 该环节的提速幅度
// 例 A(序列化占 90%,提速 32%):≈ 29%
// 例 B(序列化占 8%, 提速 32%):≈ 2.6%
// 判据:期望收益 < 5% → 不值得换(迁移成本 > 收益)
"占比 × 幅度"这个乘法还能解释一个常见困惑:"同样的优化,为什么在大服务里更明显"。
// 同一优化(少一次序列化遍历),两个服务的差别
小服务(CPU 密集,序列化占 40%):收益 ≈ 40% × 30% = 12%
大服务(IO 密集,序列化占 8%):收益 ≈ 8% × 30% = 2.4%
// 结论:不是"优化在小服务更有效",而是"它在小服务里占比更高"
4. "环境关"的三种依赖
| 依赖 | 影响 |
|---|---|
| 数据结构(大小/嵌套) | 影响"优化是否命中" |
| 并发度 | 影响"锁与 GC"的表现 |
| 缓存命中率 | 影响"是否走了慢路径" |
第三项的"排查"是"看"新实现的调用次数"":如果它"一次都没被调用",那"没变化"是必然的——而它常被归因为"这个优化没用"。
第三项最隐蔽:"缓存全命中"时,"快的那条路径"根本没被走到**——于是"换了库看起来没变"。
"占比"可以先"估"再"测",而"估"通常够用(十分钟):
// 三步估算(不装 profiler 也能做)
1 在关键路径打三个时间点:入口 / 中间 / 出口
2 算"目标环节"占"总耗时"的比例(粗到 5% 就够)
3 乘"该环节的提速幅度" → 期望收益
// 例:总 100ms,序列化 12ms(12%),提速 30% → 期望 3.6ms(3.6%)
5. "可迁移"的三种表述
| 表述 | 迁移性 |
|---|---|
| "快了 32%" | 差(无前提) |
| "在"序列化占 90%"的负载上快 32%" | 中 |
| "省了两次拷贝;在你的占比下收益 ≈ 占比 × 30%" | 好 |
第三项是"最有用的":它给了读者"自己算"的公式第三项的"形态"是"一句机制 + 一个占比 + 一个公式":三行就能让读者"判断该不该抄"——而"三页 PPT"往往只给"一个数"。
——于是"搬不搬"由读者判断,而不是"抄结论"。
"环境关"最难对齐,而它可以用"三问"快速核对:
// 抄别人的实测前,问三个问题
1 输入规模一样吗(1KB vs 1MB 的字符串,优化效果可能反向)
2 并发一样吗(单线程 vs 200 并发,"锁"的影响完全不同)
3 缓存命中一样吗("全命中"会跳过"慢路径",优化无从体现)
// 任一不同 → 只能"参考",不能"照搬数字"
6. 排错:五种"结论搬不动"
| 现象 | 成因 | 处理方向 |
|---|---|---|
| 我这里只快 4% | 占比不同 | 算占比 |
| 完全没变化 | 优化路径没走到 | 查环境 |
| 反而更慢 | 数据结构不同 | 查输入分布 |
| 数字对不上 | 工具/参数不同 | 对齐测法 |
| 无法判断 | 报告只给结论 | 补机制与占比 |
"绝对值相关"的优化最好认:它省的是"固定次数",而不是"每次的比例"。
// 三类"绝对值"优化(迁移基本成立)
1 去掉一次网络往返(省固定的 RTT,与负载无关)
2 把一个 N+1 查询合成一次(省 N 次往返)
3 把"启动时重复加载"去掉(省固定的启动时间)
// 对照:三类"占比相关"(迁移必须重算)
减少一次字符串拷贝 / 换更快的哈希 / 减少一次装箱
7. 边界:什么情况结论天然可迁移
| 场景 | 为什么 | 做法 |
|---|---|---|
| 同一服务、同一负载 | 占比与环境相同 | 可直接用 |
| 收益极大(> 2 倍) | 占比影响小 | 可直接用 |
| 机制是"去掉整步" | 与占比无关(省的是绝对量) | 可迁移 |
| 只是"方向性判断" | 不需要精确 | 可迁移 |
最后一条与"评审"有关的边界:读别人的实测时,"先找占比"是最高效的动作——找不到,就"这个结论只能当参考",而不能"直接排期"。
判断口径一句话:"这个优化的收益,是"占比相关"还是"绝对值相关"。占比相关("少一次拷贝"),迁移要重算;绝对值相关("去掉一次网络往返"),迁移基本成立。
还有一个"写报告"的建议:把"占比"和"公式"放在"结论旁边",而不是"附录"。因为读者只看"结论那一屏",而附录里的前提"不会被读到"——于是"快 32%"被抄走,而"占 90%"被留下。
补一个"反例"的用法:"为什么在你这里没效果"本身是重要信息——它说明"你服务的热点不在这",于是"下一步该优化什么"有了方向,而不是"再试一次这个优化"。
最后一个"团队"现实:结论会被"跨团队引用",而引用时通常只剩"一句话"。所以"写清楚那一句话"比"写清楚全文"更重要——理想的那一句话是"在 A 占 X% 的负载下,B 环节快 Y%,你的收益 ≈ X% × Y%",它自带"迁移说明"。
还有一条"成本"提醒:"换库"的成本不只在"改代码"——依赖治理、灰度、回滚、团队熟悉度都要算进去。于是"期望收益 2.6%"与"两周迁移 + 一次灰度"相比,结论通常是"不值得"——而这一点在"快 32%"的报告里看不到。
这才是"实测"的真正用途:不是证明"我的选择对",而是给出"别人怎么判断"的依据。
8. 自测
- 三道关分别是什么?为什么"占比关"最常被漏?
- "机制关"的三种深度里,哪一种"可迁移"?它让读者能判断什么?
- "期望收益"怎么算?为什么"占比 × 幅度"能决定"值不值得换"?
- "缓存全命中"为什么会让"换了库看起来没变"?
- "可迁移"的三种表述里,哪一种最有价值?它给了读者什么?
- 判断"结论能否直接搬"的那个问题是什么?