关于 performance-tuning
performance-tuning 是供开发者和编码 Agent 排查软件性能问题的技能。它要求设定可量化目标、分析性能瓶颈、每次只改一项,并比较可重复的测量结果。
主要功能
- 01设定量化性能目标
- 02建立可重复的基线测量
- 03先分析瓶颈再选择改动
- 04每次只测量一项改动
- 05添加性能回归防护
适用场景
- 01排查响应缓慢或超时问题
- 02按预算评估内存用量或构建包大小
- 03验证拟议优化是否改善实测性能
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ noobygains/ godmode --skill performance-tuning安装后在 Claude Code 中输入/performance-tuning确认已加载。
详细介绍
性能调优
对时间消耗在哪里的直觉,出错的概率往往比正确的概率更高。先测量问题,再通过性能分析找出时间或内存实际消耗的位置,改动一项内容,然后用相同的方法再次测量。没有测量就没有优化:每项改动都必须先用数据证明其合理性,再用数据验证其效果。
首要原则
NO OPTIMISATION WITHOUT A MEASUREMENT PROVING THE PROBLEM
没有例外。没有变通办法。没有捷径。
适用场景
必须使用:
- 用户、测试或监控报告了运行缓慢、超时、内存占用高、包体积大或成本高。
- 超出性能预算或目标(延迟百分位数、Core Web Vitals、包体积、查询耗时、冷启动)。
- 有人要求“优化”“提速”或“提升扩展能力”。
- 你正准备添加缓存、索引、并发处理、用另一种语言重写,或进行微优化。
跳过:
- 代码尚未正确运行。正确性优先;调用
godmode:fault-diagnosis或godmode:test-first。 - 没有目标,也没有投诉。“也许哪天会变慢”不算问题;记录下来,然后继续。
- 初次编写代码时选择合适的算法或数据结构。这属于常规设计,而非调优,前提是不损害清晰度。
操作流程
digraph perf {
rankdir=TB;
target [label="1. Target\n(number + percentile)", shape=box];
baseline [label="2. Baseline\n(repeatable measurement)", shape=box];
met [label="Target met?", shape=diamond];
profile [label="3. Profile\n(where does it go?)", shape=box];
hypo [label="4. One hypothesis", shape=box];
change [label="5. Change one thing", shape=box];
remeasure [label="6. Re-measure\n(same method)", shape=box];
better [label="Significant\nimprovement?", shape=diamond];
revert [label="Revert", shape=box];
done [label="7. Record + guard", shape=box];
target -> baseline -> met;
met -> done [label="yes"];
met -> profile [label="no"];
profile -> hypo -> change -> remeasure -> better;
better -> met [label="yes, keep"];
better -> revert [label="no"];
revert -> hypo;
}
-
设定目标。 明确数值、百分位数和场景:“每秒 50 个请求时,
GET /orders的 p95 低于 300 ms”,“移动设备上第 75 百分位的 LCP 不超过 2.5 s”,“压缩后的包体积低于 200 KB”。没有目标就没有结束条件;请向用户询问,或提出一个目标。 -
获取可重复的基线。 使用相同的输入数据和环境(接近生产环境,而非调试构建),丢弃预热运行结果,进行多次运行并报告波动范围,而非单个数值。保存准确的命令及其输出。工具见下表。
-
先分析,再猜测。 使用性能分析器、查询计划、跟踪记录或包分析器,找出时间、内存或字节消耗的位置。证据应是能指出热点的性能分析产物(火焰图、
EXPLAIN ANALYZE输出、跟踪记录)。常见问题按大致顺序包括:数据库查询(N+1、缺少索引、全表扫描)、串行执行的网络往返、过大的序列化负载、算法复杂度、同步 I/O 和内存 churn。 -
提出一个假设,将热点与改动关联起来:“订单列表为每个条目执行一次查询(N+1);改为单条联接查询后,p95 应能从 920 ms 降至 300 ms 以下。”
-
只改一项内容。 每次测量只做一项改动,并确保测试通过,以便将效果归因于该改动。将此改动单独提交。
-
用完全相同的方法重新测量,并与基线比较,包括波动范围。如果改进处于噪声范围内或未达到预期,就撤销改动,即使它“理应”有所帮助。如果改动有效,就从第 2 步开始,以新的基线继续,直到达到目标。
-
记录并设置防护。 在 PR 描述中写明改动前后的数值、测量方法和提交。添加防护,避免回归悄然发生:在 CI 中运行基准测试、进行预算检查(
godmode:quality-gates),或编写断言查询次数的测试。
测量工具
| 内容 | 测量方式 | 分析方式 |
|---|---|---|
| 端到端测量命令或脚本 | hyperfine --warmup 3 'cmd'(比较两项:hyperfine 'old' 'new') | 下方对应语言的分析器 |
| Python | pytest-benchmark、timeit | py-spy record -o profile.svg -- python app.py、py-spy top --pid <pid> |
| Node.js | hyperfine、基准测试库 | node --cpu-prof app.js(CPU)、node --heap-prof app.js(内存);在浏览器 DevTools 中打开生成的文件 |
| Go | go test -bench . -benchmem | go test -bench . -cpuprofile cpu.out,然后运行 go tool pprof cpu.out |
| Rust | Criterion 等基准测试框架 | 在 Linux 上使用 perf 或火焰图工具 |
| PostgreSQL 查询 | EXPLAIN (ANALYZE, BUFFERS) <query> | 查看同一执行计划:关注顺序扫描、行数估算错误和循环 |
| 负载下的 HTTP 服务 | 使用固定请求速率的负载生成器,并报告百分位数 | APM 跟踪或服务器上的语言分析器 |
| 网页 | lighthouse <url>(实验室数据);使用真实用户的现场数据评估 Core Web Vitals | 浏览器 DevTools 的 Performance 面板 |
| 包体积 | 打包工具的分析器或大小限制检查 | 同一分析器,按模块查看 |
EXPLAIN ANALYZE 会执行语句。对于 INSERT、UPDATE 或 DELETE,请在事务中运行并回滚。
预算
预算可以将“足够快”变成一项检查。应在用户能感受到结果的地方设置预算,并在 CI 中强制执行。
| 类型 | 预算示例 | 数值来源 |
|---|---|---|
| 最大内容绘制 | 2.5 s 或更短 | Core Web Vitals“良好”标准;页面加载的第 75 百分位数,分别统计移动端和桌面端 |
| 下一次绘制交互 | 200 ms 或更短 | Core Web Vitals“良好”标准;第 75 百分位数 |
| 累积布局偏移 | 0.1 或更低 | Core Web Vitals“良好”标准;第 75 百分位数 |
| API 延迟 | 预期负载下各端点的 p95 和 p99 | 产品要求或服务级别目标 |
| 查询耗时 | 生产规模数据下的每条查询耗时 | 端点预算减去其他所有开销 |
| 包或二进制文件大小 | 按入口点统计,使用 gzip 压缩 | 目标设备和网络所能承受的大小 |
| 内存 | 每个进程的峰值常驻内存 | 容器或设备的限制,并留出余量 |
应报告百分位数,而不是平均值:平均值会掩盖用户抱怨的慢请求。
反模式
| 反模式 | 失败原因 | 应采取的做法 |
|---|---|---|
| 先加缓存 | 会带来数据过时、失效错误和内存开销,还会掩盖慢代码 | 修复慢查询或慢调用;只有在测量确认有需要且制定了失效方案时才使用缓存 |
| 什么都建索引 | 索引会拖慢写入并占用存储空间 | 添加查询计划所要求的索引,并用新的查询计划确认效果 |
| 什么都异步或并行处理 | 对 CPU 密集型工作没有收益,还会增加调试难度并引入竞争条件 | 仅在分析结果显示独立 I/O 正在等待时使用并发 |
| 微调分析器未标记的循环 | 只节省纳秒,却牺牲代码清晰度 | 只修改热点路径上的代码 |
| 一次做多项更改 | 无法判断哪些更改有效,哪些有害 | 每次测量只做一项更改 |
| 对调试版或极小数据进行基准测试 | 测得的数值无法应用于生产环境 | 使用接近生产环境的构建、数据量和硬件 |
| 只运行一次,不看波动范围 | 噪声会被误认为性能提升 | 预热后运行多次,并比较波动范围 |
| 用更快的语言重写 | 为一个通常只需优化一条查询的热点带来数月风险 | 先进行分析,再优先修复热点 |
认知陷阱
| 自我辩解 | 事实 |
|---|---|
| “我知道瓶颈在哪里。” | 性能分析器之所以存在,就是因为这种判断经常出错。拿出性能分析结果。 |
| “规模扩大后这里会变慢。” | 那就在该规模下用负载测试进行测量。在此之前,这只是猜测。 |
| “缓存会让一切都变快。” | 缓存只会让缓存命中更快,并让其他所有事情变得更复杂。先测量到底哪里慢。 |
| “最佳实践建议在这里添加索引。” | 最佳实践不了解你的查询。查询计划才了解。 |
| “既然我在处理这里,不如也顺便调优一下。” | 未测量、未规划的改动会给下一次测量带来干扰。一次只做一项改动。 |
| “感觉快了。” | 感觉不是测量结果。运行相同的基准测试。 |
| “改进幅度很小,但也不会有坏处。” | 每项改动都会牺牲清晰度并占用审查时间。落在测量噪声范围内的改动应予以回退。 |
护栏
绝不:
- 在没有目标、基线和性能分析结果的情况下进行优化。
- 在两次测量之间更改不止一项内容。
- 保留效果落在测量噪声范围内的改动。
- 为未经测量的收益牺牲正确性、安全性或可读性。
- 仅用平均值或单次运行结果作为证据。
始终:
- 保存精确的测量命令及其运行前后的输出。
- 在接近生产环境的构建和环境中进行测量。
- 在每一步优化过程中确保测试全部通过。
- 为每项保留的优化成果添加回归防护(基准测试、预算或查询数量测试)。
关联
godmode:fault-diagnosis:当缓慢实际上是一个故障(卡死、泄漏、重试风暴)时,在那里进行诊断。godmode:system-design:当性能分析结果指向架构(数据模型、服务边界、托管方式)时,将决策转到那里。godmode:quality-gates:将预算和基准测试设为 CI 门禁。godmode:test-first:在优化改动代码之前,用测试固定行为。godmode:grounding:根据最新文档核验性能分析器的提示和框架的性能功能。godmode:completion-gate:优化前后的数据就是工作已完成的证据。