perf stat 组合硬件计数器分析 CPU 周期
来源:17golang原创
时间:2026-10-10 21:04:25 154浏览 收藏
分析 CPU 周期时,先用 perf stat 同时采集 cycles 和 instructions,再用周期数除以指令数得到 CPI。这样能把“程序慢”拆成可观察的计数器关系,而不是只看一次墙上时钟时间。Linux 内核文档说明,perf stat 会借助现代 CPU 中的硬件计数器统计软硬件事件。
官方文档:https://docs.kernel.org/admin-guide/workload-tracing.html
下面的示例只展示命令写法和解释方法,配图是静态说明图,不是本机运行截图,也不代表某台机器已经产生了固定数值。
先把 perf stat 的三层对象分开
这条命令里有三个容易混淆的对象:
- 事件名:例如
cycles、instructions和branches,描述想数什么。 - PMU 计数器:CPU 提供的性能监控单元,负责在硬件层累计事件。
- perf stat:把事件配置、启动/停止计数、缩放和结果展示串起来的用户态工具。
因此,看到某个事件显示为不可用时,首先要判断是 CPU、内核、虚拟机还是权限没有提供它,而不是立刻把它解释为程序没有执行相关操作。

先用 perf list 确认当前机器能看到什么
硬件事件不是所有机器都完全相同。先列出当前 perf 能识别的事件,再决定组合方式:
# 列出当前环境可见的事件,先确认名称再写统计命令
perf list
# 只筛选周期、指令和分支相关事件,便于快速阅读清单
perf list | grep -E 'cycles|instructions|branches'
如果清单中没有预期的通用事件,可能是 CPU PMU 暴露方式不同、内核配置不同,或者运行在限制硬件计数器的虚拟机里。不要把网上另一台 CPU 的事件别名原样搬过来;以当前机器的 perf list 结果为准。
用一条命令组合 CPU 周期和指令数
对一个稳定的工作负载,可以先使用最小组合:
# 同时统计 CPU 周期与退休指令,sleep 只是一个占位工作负载
perf stat -e cycles,instructions -- ./your-program --input sample.dat
# 再加入分支事件,观察控制流相关的额外线索
perf stat -e cycles,instructions,branches,branch-misses -- ./your-program --input sample.dat
-e 后用逗号分隔事件。-- 用来明确后面的内容是被测程序及其参数,避免程序自己的参数被 perf 误解。示例中的程序名、参数和文件只是写法占位,实际采集时应替换成可以重复运行的真实工作负载。
硬件计数器数量有限时,perf 可能分时复用事件,结果会带有缩放信息;如果事件组合过多,还可能出现无法调度或某个计数器不可用。第一轮建议只保留能回答当前问题的两个到四个事件。
让每次采集可以被比较
单次运行只能说明一次运行的计数结果。要比较优化前后,至少固定输入、参数、CPU 亲和性和重复次数,并把结果保存成机器可读格式:
# 使用 CSV 输出,便于保存多次运行的计数结果
perf stat -x, -o perf-baseline.csv -e cycles,instructions -- ./your-program --input sample.dat
# 绑定到同一 CPU 集合,减少调度位置变化带来的噪声
taskset -c 2-3 perf stat -r 5 -x, -e cycles,instructions -- ./your-program --input sample.dat
-r 5 让 perf 重复运行工作负载并汇总结果,-x, 用逗号作为字段分隔符,-o 将统计输出写入文件。这里的重复运行是测量设计,不是质量保证;仍要确保工作负载本身没有随机输入、缓存预热差异或后台任务干扰。
如果程序会改变系统状态,不能简单重复执行同一个命令。可以准备只读输入、隔离输出目录,或让程序提供可重复的基准模式。
用 CPI 把两个计数器变成判断线索
得到计数后,用下面的关系做第一层判断:
# CPI 表示平均每条退休指令消耗的 CPU 周期
CPI = cycles / instructions
CPI 较高通常意味着流水线等待、缓存未命中、分支预测失误或其他微架构停顿更明显;CPI 较低并不自动等于程序更快,因为总指令数、频率、并发度和输入规模也会影响总耗时。更稳妥的比较方式是同时观察:
| 观察项 | 作用 | 不要单独推出的结论 |
|---|---|---|
| cycles | 估计工作负载消耗的处理器周期 | 不能单独代表墙上时间 |
| instructions | 观察退休指令规模 | 不能单独代表每条指令成本 |
| CPI | 把周期与指令规模联系起来 | 不能直接定位具体代码行 |
| branch-misses | 观察分支预测失败线索 | 不能证明唯一瓶颈就是分支 |

把权限和虚拟化边界排除在性能结论之外
perf_events 访问受到内核安全策略约束。Linux 内核文档指出,CAP_PERFMON 可以为非 root 进程提供性能监控相关能力,使用过大的管理员权限并不是首选方案。生产环境还应考虑计数结果中可能包含 CPU 型号、进程名、路径和硬件配置等信息。
# 查看当前 perf 工具和内核环境,记录测量上下文
perf --version
uname -a
# 查看当前进程的 perf_event 相关限制;不同发行版可能有不同权限策略
cat /proc/sys/kernel/perf_event_paranoid
遇到 Permission denied、事件显示 或计数器无法调度时,按“权限—内核配置—CPU/虚拟机能力—事件数量”顺序排查。不要为了让命令有输出就直接使用 root,也不要把权限放宽后的结果与普通权限下的结果无条件混合比较。
一份可复用的最小检查清单
- 先用
perf list确认事件名称和当前环境能力。 - 从
cycles,instructions开始,按问题需要加入分支或缓存事件。 - 固定输入、CPU 集合、重复次数和输出格式,再比较优化前后。
- 同时保存 cycles、instructions、CPI 与墙上时间,避免单指标判断。
- 把权限、虚拟机、内核策略和计数器复用记录在测量说明里。
常见问题
为什么 cycles 和 wall-clock time 不一样?
cycles 是处理器性能计数器的事件数量,wall-clock time 是经过的实际时间,两者受到频率变化、调度、并行线程和系统空闲时间等因素影响。分析 CPU 计算量时看 cycles,评估用户感知延迟时仍要保留实际耗时。
事件太多时为什么会出现缩放或不可用?
硬件 PMU 的可编程计数器数量有限,perf 可能在事件之间分时复用;当某些事件无法同时调度时,结果可能被缩放或直接不可用。减少事件组合并分组测量,通常比盲目添加事件更容易得到可解释结果。
CPI 高是否就说明缓存有问题?
不一定。CPI 是综合指标,缓存等待、分支预测、流水线资源竞争和内存访问都可能影响它。应再选择与假设对应的缓存、分支或停顿事件,并与相同输入下的基线比较。
-
222 收藏
-
426 收藏
-
387 收藏
-
242 收藏
-
238 收藏
-
405 收藏
-
372 收藏
-
342 收藏
-
310 收藏
-
406 收藏
-
248 收藏
-
147 收藏
-
482 收藏
-
354 收藏
-
373 收藏
-
151 收藏
-
190 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习