3. perf stat 详解:从计数器到上下文迁移分析

perf stat 是 perf 子系统中最基础、最常用的统计命令。它不进行采样,而是直接读取 CPU 性能监控单元(PMU)和内核维护的软件事件计数器,在程序运行结束时或按固定间隔输出累计值。理解 perf stat 的输出逻辑和常用选项,是后续学习采样、火焰图分析的前提。

3.1 常用选项速查

下表列出了 perf stat 在生产环境中最高频使用的选项及其典型场景:

选项含义典型用法
-e <event>指定要统计的事件,可重复使用或使用 , 分隔-e cycles,instructions,cache-misses
-p <pid>绑定到指定进程 ID,统计该进程运行期间的计数器-p 12345
-a全系统(system-wide)模式,统计所有 CPU 上的事件-a sleep 10
-I <ms>按固定毫秒间隔输出计数器快照,而非仅输出最终汇总-I 1000
-r <N>重复运行目标程序 N 次,输出平均值和方差-r 5 ./bench
-x <sep>指定输出分隔符,便于机器解析(如 CSV)-x ,
--per-core按物理核聚合输出计数器值配合 -a 使用
--per-socket按 CPU 插槽(socket)聚合输出多路服务器排查 NUMA 问题
实战建议:排查性能问题时,优先使用 -e task-clock,cycles,instructions,cache-misses,context-switches,cpu-migrations 这组“黄金六事件”,它们能快速暴露 CPU 利用率、指令效率、缓存命中率和调度开销。

3.2 理解计数器与采样的本质区别

perf stat 基于硬件计数器和软件计数器,属于计数(counting)模式。而 perf record 属于采样(sampling)模式。两者在原理和适用场景上有根本差异:

维度计数模式(perf stat)采样模式(perf record)
数据来源PMU 硬件计数器 + 内核软件计数器PMU 溢出中断或定时器中断
输出形式累计值、平均值、比率事件发生时的 IP、调用栈、时间戳
开销极低(仅读取寄存器)较高(中断处理 + 数据记录)
典型问题“总量是多少?”“比率是否异常?”“热点在哪里?”“调用路径是什么?”
适用阶段快速验证、基线对比、异常检测深度定位、火焰图生成、代码级分析

下图展示了两种模式在 PMU 使用上的核心差异:

计数模式 vs 采样模式 计数模式(perf stat) PMU 计数器持续累加 程序结束时读取寄存器 输出:cycles=1.2G instructions=0.8G IPC=0.67 无中断开销,适合长期监控 采样模式(perf record) PMU 计数器达到采样周期 触发 PMU 溢出中断 内核记录当前 IP 和调用栈 写入 perf.data 文件 后处理生成火焰图 有中断开销,适合短时深度分析

3.3 上下文切换与 CPU 迁移分析

上下文切换(context-switches)和 CPU 迁移(cpu-migrations)是衡量调度健康度的关键指标。高频率的上下文切换意味着 CPU 在进程间频繁切换,浪费了 TLB 刷新和缓存预热开销;CPU 迁移则表明进程被调度器在不同核心间搬移,导致 L1/L2 缓存失效。

3.3.1 事件含义与计数器来源

事件名称计数器类型含义正常参考值
context-switches软件事件自愿 + 非自愿上下文切换次数CPU 密集任务 < 100/s
cpu-migrations软件事件进程被迁移到不同 CPU 的次数CPU 密集任务 < 10/s
task-clock软件事件任务实际占用的 CPU 时间(毫秒)接近 wall-clock 时间

3.3.2 使用 perf stat 定位调度问题

以下命令统计目标进程在 10 秒内的上下文切换和 CPU 迁移情况:

# 统计指定进程的上下文切换和 CPU 迁移
perf stat -e context-switches,cpu-migrations,task-clock -p $(pgrep -n myapp) -I 1000

输出示例(每 1 秒输出一行):

#           time             task-clock        context-switches      cpu-migrations
     1.001234000                999.23 msec                  120                 15
     2.002345000                998.87 msec                  145                 22
     3.003456000                997.56 msec                  890                118
     4.004567000                996.12 msec                 1020                135
异常信号:第 3 秒开始,上下文切换从 145 飙升到 890,CPU 迁移从 22 飙升到 118。这通常意味着进程被大量打断(如锁竞争、IO 等待),或者系统 CPU 资源不足导致频繁调度。此时应结合 pidstat -w 和 /proc/sched_debug 进一步排查。

3.3.3 上下文切换与 CPU 迁移的关联流程

下图展示了一次典型的 CPU 迁移过程中,计数器如何变化:

CPU 迁移过程中的计数器变化 T0 T1 T2 T3 阶段 1:CPU0 运行 进程在 CPU0 上执行 计数器:ctx=0, mig=0 阶段 2:被抢占 调度器切换出进程 ctx++ 阶段 3:迁移决策 负载均衡选择 CPU1 mig++ 阶段 4 CPU1 恢复 L1 缓存冷 关键观察 • 一次 CPU 迁移必然伴随至少一次上下文切换(ctx++) • 迁移后进程在目标 CPU 上 L1/L2 缓存失效,IPC 会短暂下降

3.4 实战:使用 perf stat 快速诊断调度抖动

以下是一个完整的排查流程,用于判断应用是否受到调度抖动影响:

  1. 获取基线:在系统空闲时运行 perf stat -e context-switches,cpu-migrations -a sleep 5,记录全系统基线。
  2. 绑定目标进程:使用 perf stat -e context-switches,cpu-migrations,task-clock -p <PID> -I 1000 观察目标进程的秒级变化。
  3. 计算比率:如果 context-switches / task-clock 超过 100/s,或者 cpu-migrations / context-switches 超过 10%,则存在调度异常。
  4. 交叉验证:使用 pidstat -w -p <PID> 1 确认自愿/非自愿切换比例,使用 /proc/<PID>/status 查看当前 CPU 亲和性。
优化方向:如果确认 CPU 迁移过高,可考虑:
• 使用 taskset 或 sched_setaffinity 绑定 CPU 亲和性
• 调整内核 sched_migration_cost_ns 参数
• 使用 cpuset cgroup 隔离关键业务进程

3.5 本章小结

  • perf stat 的 -e、-p、-a、-I 选项覆盖了从单进程到全系统、从最终汇总到连续监控的完整场景。
  • 计数模式与采样模式是互补关系:前者回答“有多少”,后者回答“在哪里”。
  • 上下文切换和 CPU 迁移是调度健康度的核心指标,通过 perf stat 的秒级输出可以快速定位调度抖动。
  • 一次 CPU 迁移必然伴随上下文切换,且会导致目标 CPU 缓存失效,是性能抖动的重要来源。