pstack 拆解 29 · 性能与持续改进
慢是可以量化的。一次修复用 perf-issue,一个指标的持续改进用 hillclimb。两者都把每个改动绑到一个测量上。
perf-issue
性能是一门测量学科,不是读源码猜。"这段代码看起来慢"和"这段代码实测占了多少时间"常常是两回事,而优化一个并不是瓶颈的地方,非但没有收益,还白添了复杂度。所以每个修复都要绑到一个测量上。
perf-issue 追踪一个测量到的慢,对着基线改进。它的总纲是:你拥有测量叙事。计划、审查、验证这些数字。把每一个修复绑到一个测量上,不要用读源码代替测量。
它是一次性修复,这就是它和 hillclimb 的区别:那个是持续循环。
它的步骤。
通过对应的 control skill 捕获一份基线 trace。用
benchmark-checklist审基线,以及之后每一个数字。用
how给假设做种子。不要在没跑过的前提下声称一个性能天花板。 它给了一串"性能咒语",按最便宜的优先试:- 别做它。结果没人用的工作,停掉它,而不是把它变便宜。
- 做它,但别重复做。
- 少做它。
- 晚点做它。
- 趁他们没看的时候做它。
- 并发地做它。
- 更便宜地做它。
靠前的咒语一旦达到目标,就停。
从 trace 规划修复。跨函数边界先
architect。委派实现,用 perf-issue 模型。审查 diff。捕获修复后的 trace。每个尝试验证过再试下一个。解析并对比产物(JSON 进 sqlite 做 diff)。
inconclusive或错的 surface 不算通过。在 PR 里引用这次测量。
走 opening-a-pr。
回复要点名:基线数字、修复后数字、差值、产物路径。
hillclimb
hillclimb 是对一个可测量的东西持续、科学地改进,对着一个目标。它和 perf-issue 的区别,是它有一个循环,不只是一次修复。
它的核心纪律只有一句:一个改动,一次测量,留或回退。 永不堆叠未测的改动,永不从代码审查里宣称一个赢。
它的步骤。
- 先 ground 工作负载和架构。 跑
how,命名那些能移动结果的现实维度(数据大小、历史、状态、并发),选一个能复现用户抱怨的用例。如果没有用例能复现它,先修 repro,不要开始爬山。 然后定一个指标、一个"哪个方向算更好"、以及一个可检查的停止谓词。这个谓词要把一个目标和一个尝试次数的下限配在一起,比如"比基线好至少 50%,且至少 10 次迭代",好让一个早来的幸运胜利不能结束整轮。数字用用户给的,没给就跟他约定。 - 建测量 harness,证明它的敏感度,然后冻结它。 跑一些对比鲜明的现实工作负载,确认目标用例复现症状,而更容易的用例如预期地分开。如果 harness 分不开它们,改工作负载或指标。冻结之前用
benchmark-checklist审它,让它打印错误计数和"做了多少工作"的计数。冻结之后,一条可重复的命令输出那个指标,采样足够多以穿过噪声(中位数,不是单次)。在任何改动之前,记下基线指标和回归门的一次绿跑。 - 打开决策日志。 用
show-me-your-work,一行一次尝试:id、假设、改动、前值、后值、差值、测试、判决(留或回退)、备注。每次尝试之前先读它。 - 把假设扎进架构模型,让它命名一个具体机制("把 X 从 boot 路径上推出去,因为它挡住首次绘制"),而不是"试试加个缓存"。性能类的假设按第 2 步的咒语排序。
- 循环,一次一个假设。 把改动交给 subagent(用 hillclimb 模型,范围收紧),监督并审查 diff,而不是自己敲。用冻结的 harness 前后测量,跑回归门。只有指标越过噪声、门保持全绿,才接受;否则把改动整个回退。一个接受的修复一个 commit,只
git add <files>,绝不-A。无论留还是回退,都记一行日志。保留几个独立假设同时活时,并行发给不同 subagent,各在自己的 worktree。 - 越过第一个平台期。 卡住的时候(连续几个拒绝),换类别、合并差一点成功的、重读源码,或者在断定山爬完之前先试一个更激进的想法。正确性和简单性排在数字之上。一个赢了数字但破坏行为的改动回退;一个保持住数字的简化留下。
- 在谓词满足时停,或者当剩下的想法都很边际、不值它的成本时停。 不放松谓词去满足它,也不在还有便宜、没试过的假设时退出。真卡住了就浮出来,不要空转。
- 走 opening-a-pr,把接受的 commit 按落地顺序堆起来。
回复要点名:指标和目标、基线到最终的百分比差值、迭代次数(留 vs 回退)、每个接受的修复一行、决策日志路径、以及如果再推进一步你会试的最好想法。
参考
原文都在 pstack 上游仓库(cursor/plugins):