性能工程全景:从现象到证据 / Performance Engineering Overview
性能工程不是“让代码跑快一点”的技巧集合。
它更像一次有纪律的侦察:先确认用户真正感受到的慢是什么,再固定工作负载,采集证据,判断时间花在计算、访存、I/O、锁、调度、网络、GPU 还是排队,最后只改一个关键因素,复测,并把结论放进回归体系。
如果没有这条线,工具越多越乱。
WPR、WPA、VTune、perf、Nsight、Perfetto、bpftrace、heaptrack、Callgrind、Prometheus 都很有用,但它们回答的问题不同。一个常见错误是拿着 CPU 火焰图去解释网络等待,或者拿着 GPU kernel 指标去解释端到端卡顿。工具不是起点,问题才是起点。
1. 一条完整的性能分析线
先把性能工程抽象成一条线:
+--------------------+
| 用户现象或项目目标 |
+--------------------+
|
v
+--------------------+
| 代表性工作负载 |
+--------------------+
|
v
+--------------------+
| 基线与统计口径 |
+--------------------+
|
v
+--------------------+
| 端到端时间线 |
+--------------------+
|
v
+--------------------+
| 局部热点归因 |
+--------------------+
|
v
+--------------------+
| 瓶颈模型与假设 |
+--------------------+
|
v
+--------------------+
| 最小优化实验 |
+--------------------+
|
v
+--------------------+
| 回归与生产验证 |
+--------------------+每一环都在回答一个不同的问题。
用户现象回答:慢对谁造成了什么影响。
工作负载回答:我们测的东西是否代表真实项目。
基线回答:当前到底有多慢,噪声有多大。
时间线回答:慢发生在什么时候,线程和设备之间有没有等待。
热点归因回答:CPU 或 GPU 真正在执行哪些路径。
瓶颈模型回答:继续优化某条路径是否符合硬件上限和算法结构。
优化实验回答:一个具体假设是否成立。
回归验证回答:这次收益是否能在以后保住。
2. 性能工程包含哪些块
性能工程通常可以分成八块。
第一块是目标定义。延迟、吞吐、成本、能耗、内存峰值、P99、帧时间、导入时间、求解时间都可以是目标,但不能混成一句“更快”。目标要带有输入、机器、统计口径和正确性边界。
第二块是实验设计。它包括工作负载选择、预热、冷启动、样本数量、随机种子、环境记录、A/B 交错运行、正确性校验和噪声判断。
第三块是系统级追踪。系统级追踪看的是线程、进程、内核、I/O、GPU、调度器、队列和事件顺序。Windows 上常用 WPR 采集 ETW,再用 WPA 分析 ETL;Linux 和 Android 上常见 ftrace、Perfetto、LTTng 等体系。
第四块是 CPU Profiling。CPU Profiling 关注 CPU 时间去了哪里。采样 Profiler 会定期记录指令位置和调用栈,最后聚合成函数、调用树或火焰图。VTune Hotspots、Linux perf、Instruments、Visual Studio Profiler、AMD uProf 都属于这个范畴。
第五块是硬件计数器与微架构分析。硬件计数器观察 cycles、instructions、branch miss、cache miss、TLB miss、memory bandwidth 等事件。它们帮助判断热点是前端取指受限、分支错误、执行端口压力、缓存未命中,还是内存带宽受限。
第六块是内存、Cache 与 NUMA。很多程序不是算不动,而是数据喂不上。这里要分析分配次数、峰值 RSS、Page Fault、Cache locality、False Sharing、NUMA 远端访问、内存带宽和对象生命周期。
第七块是 GPU 与异构流水线。GPU 性能不能只看单个 Kernel。一个项目可能慢在 CPU 准备数据、PCIe 传输、同步点、Kernel 启动间隙、显存访问、Warp 停顿或 CPU/GPU 流水线没有重叠。
第八块是生产回归。本地优化通过不等于项目长期变好。性能需要 CI 基准、版本对比、灰度指标、可观测性、报警阈值和回滚方案守住。
3. 工具分类:先看问题,再选工具
下面这张表比“工具清单”更重要。它按问题选工具。
| 你要回答的问题 | 工具类型 | 主流工具 |
|---|---|---|
| 这次改动是否真的变快 | 基准测试 | Google Benchmark、pytest-benchmark、自研端到端基准 |
| Windows 桌面程序为什么卡 | 系统追踪 | WPR、WPA、Xperf、GPUView |
| Linux 服务 CPU 时间在哪里 | CPU 采样 | perf、VTune、FlameGraph、gprof-ng |
| CPU 热点为什么慢 | 硬件计数器 | VTune Microarchitecture、perf stat、perf record -e、AMD uProf |
| 线程为什么没跑起来 | Off-CPU / 调度追踪 | WPA、Perfetto、perf sched、bpftrace |
| 锁、系统调用、内核事件在哪里 | 动态追踪 | bpftrace、DTrace、SystemTap、ETW providers |
| 内存为什么涨或抖 | 内存分析 | heaptrack、Massif、WPA Memory、jemalloc profiling、ASan/LSan |
| Cache 行为是否合理 | Cache 模拟与计数器 | Cachegrind、perf、VTune Memory Access |
| GPU 为什么空着 | GPU 时间线 | Nsight Systems、GPUView、rocprof |
| 单个 CUDA Kernel 为什么慢 | GPU Kernel Profiler | Nsight Compute、rocprof、AMD uProf |
| 线上性能是否退化 | 可观测性 | OpenTelemetry、Prometheus、Grafana、APM、日志与 Trace |
一个项目常见的工具组合是:
+------------------+ +------------------+ +------------------+
| 端到端基准 | ---> | 系统级时间线 | ---> | 局部 Profiler |
+------------------+ +------------------+ +------------------+
| | |
v v v
是否真的慢 慢在哪个阶段 哪段代码或 kernel 慢Windows C++ 桌面软件可以从 WPR/WPA 起步。
Linux 后台或 HPC 程序可以从 perf stat 和 perf record -g 起步。
Intel CPU 深度优化可以用 VTune。
CUDA 程序先用 Nsight Systems 看 CPU/GPU 时间线,再用 Nsight Compute 进入单个 Kernel。
跨进程、跨线程、跨系统事件很多时,Perfetto 适合做统一时间线和 SQL 化分析。
线上问题需要 OpenTelemetry、Prometheus、日志和采样 Profile,而不是把生产机器当本地实验台。
4. WPR 和 WPA 的位置
WPR 是 Windows Performance Recorder。它负责采集。它基于 ETW,也就是 Event Tracing for Windows。ETW 可以把内核调度、CPU、磁盘、文件、网络、GPU、进程线程、应用自定义事件等记录到 ETL 文件。
WPA 是 Windows Performance Analyzer。它负责分析。WPA 打开 ETL 文件,把事件变成图和表,让你从时间线里看 CPU Usage、Thread Activity、Disk Usage、File I/O、Generic Events、GPU 等视图。
典型流程是:
选择 WPR Profile
|
v
Start Recording
|
v
复现卡顿或慢路径
|
v
Save ETL
|
v
WPA 打开并框选
|
v
先看时间线再归因WPR/WPA 最适合回答:
- UI 主线程是否被长任务占住;
- 线程是 Running、Ready 还是 Waiting;
- 慢的时候是否有硬缺页、磁盘、文件、网络或 GPU 等待;
- 哪个线程唤醒了哪个线程;
- 系统服务、驱动、杀毒、后台任务是否干扰;
- CPU 忙是你的进程忙,还是整机资源被抢走。
它不适合直接替代算法层面的 Benchmark,也不适合只靠截图判断根因。WPA 给你的是事件证据,你仍然要回到代码、工作负载和假设。
5. VTune 的位置
VTune 是 Intel 的性能分析工具。它既能做常见 Hotspots,也能做 Threading、Memory Access、Microarchitecture Exploration、HPC Performance Characterization、GPU Offload 等分析。
VTune 的价值在于把“函数占比”进一步推到微架构层:
热点函数
|
v
CPU Pipeline 分类
|
+-- Frontend Bound
+-- Bad Speculation
+-- Backend Core Bound
+-- Backend Memory Bound
|
v
源码、汇编、循环、内存访问模式常用命令形态:
vtune -collect hotspots -- ./app arg1 arg2
vtune -collect memory-access -- ./app arg1 arg2
vtune -collect threading -- ./app arg1 arg2
vtune -report hotspots -r r000hshotspots 先找 CPU 时间集中在哪里。
threading 看线程利用率、等待和并行效率。
memory-access 看缓存、带宽、NUMA 等访存问题。
uarch-exploration 看更细的流水线瓶颈。
不要一上来就开最重的分析。采集越重,开销和数据量越大,也越可能改变程序行为。先用低成本分析定位,再逐层加深。
6. perf、bpftrace 和 Perfetto 的位置
perf 是 Linux 下最常用的性能工具之一。它可以读取硬件计数器,也可以采样调用栈、记录内核 tracepoint、做源码或汇编标注。
最小起步命令是:
perf stat ./app
perf record -g -- ./app
perf report
perf annotateperf stat 回答“总体事件计数如何”。
perf record -g 回答“CPU 样本落在哪些调用栈”。
perf report 用于交互查看热点。
perf annotate 把事件映射到源码或汇编。
bpftrace 更像一把动态探针。它适合临时回答“谁在频繁调用这个系统调用”“哪个 PID 在等待某个内核事件”“某个函数参数分布是什么”。它不需要重新编译目标程序,但需要理解内核探针、权限和开销边界。
Perfetto 更像统一时间线和分析平台。它可以采集或打开多种 trace,把调度、频率、系统调用、内存、应用事件、CPU Profile 等放在同一条时间线上,还能用 SQL 查询。它特别适合 Android、Chromium、Linux/嵌入式和跨进程事件非常多的场景。
7. Nsight Systems 和 Nsight Compute 的位置
CUDA 或其他 GPU 项目常见误区是直接打开单个 Kernel Profiler。
正确顺序通常是先看端到端时间线。
Nsight Systems 观察的是系统级时间线:
- CPU 线程何时准备数据;
- CUDA API 调用何时发生;
- Host 到 Device 的拷贝是否阻塞;
- Kernel 之间是否有空洞;
- 多个 Stream 是否真的并发;
- CPU 和 GPU 是否互相等待;
- NVTX 标记的业务阶段与 GPU 工作是否对应。
Nsight Compute 观察的是单个 Kernel:
- 访存是否合并;
- Shared Memory 和寄存器压力如何;
- Occupancy 是否受限;
- Warp 停顿原因是什么;
- DRAM、L2、SM 吞吐接近什么上限;
- 指令混合和分支是否合理。
关系是:
Nsight Systems
-> 先判断 GPU 是否是端到端瓶颈
-> 再定位哪个 kernel 或同步点值得深入
Nsight Compute
-> 深入单个 kernel
-> 判断访存、计算、占用率和指令瓶颈如果 Systems 里看到 GPU 大量空闲,Compute 里把某个 Kernel 优化 20% 也可能没有意义。因为真正问题可能是 CPU 准备慢、同步太频繁、数据传输没有重叠,或者任务粒度太碎。
8. 本章重构后的阅读路线
本目录不再按工具堆叠,而按性能工程工作流安排:
- 性能工程全景:从现象到证据
- 基准测试:把变快变成可信实验
- 系统级追踪:用时间线看等待、调度与 I/O
- CPU、内存、Cache 与 NUMA:把热点解释到硬件行为
- GPU、Roofline 与异构瓶颈
- 优化闭环:从假设、改动到回归守护
这些标题和文件名不是完全一一对应旧名字,这是有意的。为了保持网站链接稳定,文件名暂时不改;为了让学习路径清楚,H1 和内容职责按新结构走。
9. 工具选择速查
问题:我只想知道本次运行总开销变化
工具:Google Benchmark / 自研基准 / perf stat
问题:Windows 软件卡顿、线程等待、磁盘或 GPU 交互
工具:WPR + WPA
问题:Linux CPU 热点在哪里
工具:perf record -g + perf report
问题:Intel CPU 热点为什么慢
工具:VTune Hotspots -> Microarchitecture / Memory Access
问题:线程多但吞吐不涨
工具:WPA Thread Activity / VTune Threading / perf sched
问题:内存峰值高或分配频繁
工具:heaptrack / Massif / WPA Memory / jemalloc profiling
问题:Cache miss、带宽、NUMA
工具:VTune Memory Access / perf stat / numastat / pcm
问题:CUDA 端到端慢
工具:Nsight Systems
问题:单个 CUDA Kernel 慢
工具:Nsight Compute
问题:线上慢但本地复现不了
工具:OpenTelemetry / Prometheus / 日志 / 采样 Profile10. 性能报告应该长什么样
性能报告不应该只贴一张工具截图。
一个合格报告至少包含:
- 目标指标:例如 P95 导入时间、单步求解时间、帧时间;
- 工作负载:输入文件、规模、并发量、预热状态;
- 环境:机器、系统、编译器、构建模式、驱动、功耗策略;
- 基线:多次样本、分布、噪声范围;
- 证据:时间线、调用栈、计数器或内存数据;
- 假设:为什么某个机制导致慢;
- 改动:只改变了什么;
- 结果:端到端、局部、资源峰值和正确性;
- 结论:适用范围、风险、回归门禁。
可以用下面的模板写:
现象:
打开 5 GB 工程 P95 为 18.4 s,目标是 12 s 以内。
工作负载:
data/case-large-01,冷启动,Release + symbols,运行 15 次。
证据:
WPR/WPA 显示 6.2 s 在主线程等待文件 I/O;
CPU 采样显示解析线程并不是主热点。
假设:
小块同步读取导致主线程等待,批量预读可以降低等待时间。
实验:
只把实体属性读取改成批量读取,保持解析逻辑不变。
结果:
P95 从 18.4 s 降到 12.7 s,峰值内存增加 420 MB,校验和一致。
后续:
设置大文件导入基准和峰值内存门禁。11. 常见失败方式
最常见的失败不是工具不会用,而是问题一开始就问错了。
- 在 Debug 构建上做性能判断;
- 不固定输入,却比较两个版本;
- 只运行一次;
- 只报告平均值;
- 没有正确性校验;
- 把 CPU 利用率当成功指标;
- 用单个函数优化解释端到端性能;
- 看到热点就立刻重写代码;
- 没区分 CPU 时间和墙钟时间;
- 没区分冷启动和稳态;
- 没区分 On-CPU 和 Off-CPU;
- 没保存 trace、命令、版本和环境;
- 优化后没有回归门禁。
真正可靠的性能工程,是让每一次优化都能被别人复现、质疑、推翻或确认。
12. 官方资料入口
- Windows Performance Recorder
- Windows Performance Analyzer
- Intel VTune Profiler User Guide
- Linux perf wiki
- Perfetto documentation
- bpftrace documentation
- NVIDIA Nsight Systems
- NVIDIA Nsight Compute
- Google Benchmark User Guide
本章后面的文章会把这些工具放进具体场景,而不是让你背一个工具列表。