快不了多少

把那一段做快十倍,整体只快 6% —— 这在动手之前就算得出来。

一段占总耗时的 p,把它做快 s 倍,整体加速是 1 / ((1−p) + p/s)。让 s 趋于无穷 —— 把这一段做到完全不花时间 —— 最好也只有 1/(1−p)

占 20% 的那一段,天花板是 1.25×。你在它里面有多聪明, 完全不改变这个数:另外 80% 的时间根本不经过它。

所以该问的从来不是「这段能做多快」,而是「这段最多值多少」 —— 而那只是一次除法,在花掉一个季度之前就能做。

它会告诉你够不够得着

先看前提

5 段 至少两段才谈得上先优化哪个
每段测了 8 次 每段至少 3 次,且次数一致
没给总耗时,默认这几段就是全部 覆盖到 95.0% 以上
2.00× 是达不到的

最大的一段「查数据库」只占 32.4%,就算把它清零,整体也只有 1.48×。要 2.00×,至少得把 2 段一起做没:查数据库、渲染模板。那不是优化,那是重写 —— 早知道这一点,就不会有人先去啃那一段了。

每一段最多值多少

查数据库 32.4% 1.48×
渲染模板 23.9% 1.31×
序列化 19.1% 1.24×
鉴权 13.7% 1.16×
日志 10.9% 1.12×
份额天花板 = 1/(1−份额)
阶段份额做快 2×做快 10×天花板 要整体快 2.00×
查数据库 32.4% 1.19× 1.41× 1.48× 做不到
渲染模板 23.9% 1.14× 1.27× 1.31× 做不到
序列化 19.1% 1.11× 1.21× 1.24× 做不到
鉴权 13.7% 1.07× 1.14× 1.16× 做不到
日志 10.9% 1.06× 1.11× 1.12× 做不到

「做不到」不是说很难,是说不存在:那一段清零也到不了这个目标, 因为剩下的时间不经过它。要 2.00×,至少得把这几段一起清零:查数据库、渲染模板

把「查数据库」做快,收益长什么样

1.00×1.30×1.60×16×32× 天花板 1.48× 这一段快了多少倍 →

横轴是这一段快了多少倍,纵轴是整体快了多少倍。 虚线是天花板 1.48× —— 曲线一辈子够不着它。 从 1× 做到 2× 拿到 40% 的全部收益; 从 2× 再拼到 10×,工作量大得多,只多拿 46%。 这条曲线的形状就是「优化到什么程度该收手」的答案。

一步一步做下去

每一步把当时最大的那段做快一倍。做完一段,剩下几段的份额会重新分配 —— 所以第二件事该做什么,不是从原来那份 profile 上读出来的。

1. 查数据库 1.19× +19%
2. 渲染模板 1.39× +17%
3. 序列化 1.60× +15%

累计加速,以及每一步各自贡献了多少。后面的步骤越来越不值钱 —— 每做掉一段,下一段在「剩下的时间」里占比虽然变大,但能省的绝对时间在变小。

值得注意的

有 2 段就算做快十倍,整体也不到 15%

鉴权(13.7% → 1.14×)、日志(10.9% → 1.11×)。这些不是「以后再说」,是算过之后不必做:十倍是很大的工程,换来的整体收益比一次运气好的重跑还小。

它不知道的事

它不知道哪一段好动。 上面排的是效果,不是代价 —— 代价只有你知道。一段占 40% 但要重写整个存储层,和一段占 15% 但改一行配置, 这里看起来是前者赢。真正的决定是「天花板 ÷ 工作量」,而这一页只给得出前一半。

它假设这几段是串行的。 如果两段本来就并行,做快其中一段可能一点用都没有 —— 另一段还在跑。真并行的系统要看关键路径,不是看份额。

它假设做快一段不会拖慢别段。 加缓存省了查询,但多了失效逻辑; 加线程省了等待,但多了争抢。这里算的是「其它都不动」的那个上界。

贴进来的东西只用来算这一次,不写盘、不记日志、不存任何地方。 整个页面没有一行 JavaScript。