性能剖析

让 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(cpuheapmutex);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. 文档化热点原因与优化思路,便于下次维护参考

返回技能库 更多技能入口