性能剖析
让 Agent 在可复现负载下采集火焰图与分配轨迹,区分测量开销与真实热点并给出验证性微基准建议。
SKILL 指定剖析器(async-profiler、pprof、Chrome Performance 等)、采样时长与生产环境安全开关;避免在优化未测量前大面积改写热路径。
对 GC、锁与 I/O 等待类问题,要求 Agent 交叉引用指标(延迟分位、饱和度)与调用栈,防止误将症状当根因。
假设 → 复现 → 采样 → 火焰图 → 验证
[ 性能假设 / 用户可感知症状 ]
│
▼
┌─────────────┐ 固定:版本、数据规模、硬件、并发、缓存状态
│ 可复现负载 │──── 冷启动与稳态分次跑,避免 JIT / 预热混淆结论
└─────────────┘
│
▼
┌─────────────┐ 周期性栈采样(CPU)或事件(alloc、lock、I/O)
│ 剖析器采集 │──── 时长覆盖多个请求/迭代;生产需限流与安全模式
└─────────────┘
│
▼
┌─────────────┐ 折叠同构栈;宽条 = 该路径在样本中占比高
│ 火焰图解读 │──── 区分自时间 vs 子调用;注意内联与原生帧丢失
└─────────────┘
│
▼
┌─────────────┐ 微基准或 A/B;保留前后数字与回归用例
│ 验证与回归 │──── 文档化「为何此处是热点」与回滚策略
└─────────────┘
采样剖析是统计估计:样本量不足时宽条会抖动;延长窗口或提高事件率前先评估对业务的影响。
采样频率、时长与开销
CPU 采样:固定间隔抓取调用栈(如每 10ms~100ms);间隔越密统计越稳,但中断与缓冲区开销上升。长稳态服务优先「足够长的窗口 + 适中频率」而非极端高频短窗。
分配 / 锁 / wall-clock:常基于事件或计时器;在 SKILL 中写明是否启用 alloc、是否仅用户态帧,以及是否在 CI 用缩小数据集做冒烟剖析。
- 冷启动与稳态分开采样,避免混淆 JIT 预热。
- 大数据集下标注数据规模与硬件型号。
- 剖析文件脱敏后再分享或存档。
Node.js CPU profiling:使用 --prof 内置标志采集 V8 tick 文件并生成火焰图:
# 1. 启动带 --prof 的 Node.js 进程(生成 isolate-*.log)
node --prof app.js &
APP_PID=$!
# 施加负载(用 autocannon / wrk 等工具)
npx autocannon -c 50 -d 30 http://localhost:3000/api/heavy
kill $APP_PID
# 2. 将 isolate-*.log 转换为可读 tick 文件
node --prof-process isolate-*.log > processed.txt
# 3. 用 0x 工具一步生成交互式火焰图 HTML
npx 0x -- node app.js
# 自动在 ./{pid}.0x/ 目录生成火焰图,浏览器打开即可交互
# 4. 用 clinic.js 套件(更友好的诊断工具)
npm install -g clinic
clinic doctor -- node app.js # 综合诊断
clinic flame -- node app.js # CPU 火焰图
clinic bubbleprof -- node app.js # async 调用气泡图
# 5. 仅针对某段代码做微基准(benchmark.js)
const Benchmark = require('benchmark');
const suite = new Benchmark.Suite();
suite
.add('method A', () => { slowMethod(); })
.add('method B', () => { fastMethod(); })
.on('cycle', (e) => console.log(String(e.target)))
.on('complete', function() {
console.log('Fastest: ' + this.filter('fastest').map('name'));
})
.run({ async: true });
如何读火焰图
经典 CPU 火焰图:横轴宽度表示该栈路径在样本集合中的相对占比(不是时间先后顺序);纵轴为调用深度,自顶向下一般为「入口 → 热点叶子」。
优先看什么
- 最宽的「平顶」:同一函数下多个子调用并列时,宽的是主要贡献者。
- 突然变宽的帧:对比基线版本,定位回归引入路径。
- 过深的递归或重复模式:可能暗示 N+1、同步阻塞或不当抽象。
常见误读
- 把「出现在栈上」等同于「自时间高」:需结合折叠视图或 self 统计。
- 忽略内联与尾调用:符号可能集中在调用方名称下。
- 在 GC 压力大时只看 CPU 图:需叠加 allocation 或 heap profile。
指标与调用栈交叉验证
优化后应保留前后对比数字与回归测试,必要时用 feature flag 控制发布;文档化「为何此处是热点」便于后续维护。
- 延迟分位(p95/p99)与火焰图宽条是否指向同一模块。
- 饱和度(CPU、磁盘、网络)与 off-CPU / wall 剖析是否一致。
- 锁等待:线程 dump + 锁剖析与业务 trace 对齐同一时间段。
Web Vitals 测量代码(LCP/FID/CLS)与 Lighthouse CI 集成:
// Web Vitals 测量(web-vitals v3)
import { onLCP, onFID, onCLS, onINP, onFCP, onTTFB } from 'web-vitals';
function sendToAnalytics(metric) {
const body = JSON.stringify({
name: metric.name,
value: metric.value, // 原始值(ms 或 score)
rating: metric.rating, // 'good' | 'needs-improvement' | 'poor'
delta: metric.delta,
id: metric.id,
navigationType: metric.navigationType,
});
navigator.sendBeacon('/analytics', body);
}
onLCP(sendToAnalytics); // Largest Contentful Paint: 目标 < 2500ms
onFID(sendToAnalytics); // First Input Delay: 目标 < 100ms(已废弃,用 INP)
onINP(sendToAnalytics); // Interaction to Next Paint: 目标 < 200ms
onCLS(sendToAnalytics); // Cumulative Layout Shift: 目标 < 0.1
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);
Lighthouse CI 配置文件与 GitHub Actions 集成:
// lighthouserc.js — Lighthouse CI 配置
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000/', 'http://localhost:3000/about'],
numberOfRuns: 3, // 多次运行取中位数
startServerCommand: 'npm run start',
startServerReadyPattern: 'listening on',
},
assert: {
preset: 'lighthouse:recommended',
assertions: {
'categories:performance': ['error', { minScore: 0.9 }],
'categories:accessibility': ['warn', { minScore: 0.95 }],
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
'interactive': ['warn', { maxNumericValue: 3800 }],
},
},
upload: {
target: 'temporary-public-storage', // 或配置 LHCI server
},
},
};
// GitHub Actions 中集成 Lighthouse CI
// - name: Run Lighthouse CI
// run: |
// npm install -g @lhci/cli
// lhci autorun
// env:
// LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
工具与产物速查
- JVM:async-profiler(CPU/alloc)、JFR;产物多为折叠栈文本或 jfr,可转火焰图 HTML。
- Go / 原生:pprof(
cpu、heap、mutex);go tool pprof -http=交互火焰图。 - 前端:Chrome Performance、Lighthouse;关注长任务、布局与脚本自时间。
剖析会话草稿生成
填写下列字段,生成可粘贴到工单或 SKILL 附录的「采样与火焰图」检查清单(示意,落地前与团队运行手册对齐)。
导出火焰图时优先使用团队约定的折叠栈格式;对比两次采集请固定二进制与输入数据集,避免宽条差异来自环境漂移。
---
name: performance-profiling-cn
description: 用剖析器定位 CPU/内存热点并输出可验证优化建议
tags: [performance, profiling, web-vitals, lighthouse]
---
# 剖析方法论
1. 先假设:用户可感知症状 + 具体路径(URL/接口/代码路径)
2. 可复现负载:固定版本、数据规模、硬件、并发、缓存冷热状态
3. 工具选择:Node.js 用 --prof / 0x / clinic,前端用 Chrome DevTools + Lighthouse
# Node.js CPU Profiling
4. --prof + --prof-process 生成 tick 文件;0x 一步生成火焰图 HTML
5. clinic doctor 综合诊断;clinic flame 专注 CPU;clinic bubbleprof 专注 async
6. 微基准用 benchmark.js;保留前后数字与回归测试用例
# 前端性能
7. LCP 目标 < 2500ms;INP 目标 < 200ms;CLS 目标 < 0.1
8. web-vitals 库在生产中采集真实用户数据并上报 analytics
9. Lighthouse CI:lighthouserc.js 配置 assertions,在 CI 阻塞不达标 PR
# 火焰图解读
10. 宽条 = 该路径在采样中占比高(不是绝对时间)
11. 先看「平顶宽条」再向下钻取到叶节点函数
12. CPU 图与 allocation 图叠合:区分 CPU 热点与 GC/内存压力
# 回归防护
13. 性能基准对比:CI 中运行 benchmark,对比 main 分支结果
14. Lighthouse CI minScore/maxNumericValue 门禁,失败阻塞合并
15. feature flag 控制优化上线,保留回滚能力
16. 文档化热点原因与优化思路,便于下次维护参考