编译与调试完全指南—第6章 性能分析工具

第6章 性能分析工具

完整 485 页的 《编译与调试完全指南》见: lengjingzju-notes发布页

性能分析是软件优化的第一步。Linux 提供了从用户态到内核态、从粗粒度到细粒度的完整性能分析工具链。本章重点介绍两大核心工具:perf(Linux 内核自带的性能计数器子系统)和 gprof(GNU 传统的函数级性能剖析工具),并回顾与性能分析配合的编译选项。
#mermaid-svg-kKVdiELgSXC2IxyT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kKVdiELgSXC2IxyT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kKVdiELgSXC2IxyT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kKVdiELgSXC2IxyT .error-icon{fill:#552222;}#mermaid-svg-kKVdiELgSXC2IxyT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kKVdiELgSXC2IxyT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kKVdiELgSXC2IxyT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kKVdiELgSXC2IxyT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kKVdiELgSXC2IxyT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kKVdiELgSXC2IxyT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kKVdiELgSXC2IxyT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kKVdiELgSXC2IxyT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kKVdiELgSXC2IxyT .marker.cross{stroke:#333333;}#mermaid-svg-kKVdiELgSXC2IxyT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kKVdiELgSXC2IxyT p{margin:0;}#mermaid-svg-kKVdiELgSXC2IxyT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kKVdiELgSXC2IxyT .cluster-label text{fill:#333;}#mermaid-svg-kKVdiELgSXC2IxyT .cluster-label span{color:#333;}#mermaid-svg-kKVdiELgSXC2IxyT .cluster-label span p{background-color:transparent;}#mermaid-svg-kKVdiELgSXC2IxyT .label text,#mermaid-svg-kKVdiELgSXC2IxyT span{fill:#333;color:#333;}#mermaid-svg-kKVdiELgSXC2IxyT .node rect,#mermaid-svg-kKVdiELgSXC2IxyT .node circle,#mermaid-svg-kKVdiELgSXC2IxyT .node ellipse,#mermaid-svg-kKVdiELgSXC2IxyT .node polygon,#mermaid-svg-kKVdiELgSXC2IxyT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kKVdiELgSXC2IxyT .rough-node .label text,#mermaid-svg-kKVdiELgSXC2IxyT .node .label text,#mermaid-svg-kKVdiELgSXC2IxyT .image-shape .label,#mermaid-svg-kKVdiELgSXC2IxyT .icon-shape .label{text-anchor:middle;}#mermaid-svg-kKVdiELgSXC2IxyT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kKVdiELgSXC2IxyT .rough-node .label,#mermaid-svg-kKVdiELgSXC2IxyT .node .label,#mermaid-svg-kKVdiELgSXC2IxyT .image-shape .label,#mermaid-svg-kKVdiELgSXC2IxyT .icon-shape .label{text-align:center;}#mermaid-svg-kKVdiELgSXC2IxyT .node.clickable{cursor:pointer;}#mermaid-svg-kKVdiELgSXC2IxyT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kKVdiELgSXC2IxyT .arrowheadPath{fill:#333333;}#mermaid-svg-kKVdiELgSXC2IxyT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kKVdiELgSXC2IxyT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kKVdiELgSXC2IxyT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kKVdiELgSXC2IxyT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kKVdiELgSXC2IxyT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kKVdiELgSXC2IxyT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kKVdiELgSXC2IxyT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kKVdiELgSXC2IxyT .cluster text{fill:#333;}#mermaid-svg-kKVdiELgSXC2IxyT .cluster span{color:#333;}#mermaid-svg-kKVdiELgSXC2IxyT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kKVdiELgSXC2IxyT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kKVdiELgSXC2IxyT rect.text{fill:none;stroke-width:0;}#mermaid-svg-kKVdiELgSXC2IxyT .icon-shape,#mermaid-svg-kKVdiELgSXC2IxyT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kKVdiELgSXC2IxyT .icon-shape p,#mermaid-svg-kKVdiELgSXC2IxyT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kKVdiELgSXC2IxyT .icon-shape .label rect,#mermaid-svg-kKVdiELgSXC2IxyT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kKVdiELgSXC2IxyT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kKVdiELgSXC2IxyT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kKVdiELgSXC2IxyT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 分析维度
性能分析工具
perf

内核性能计数器
gprof

函数级剖析
PGO

反馈优化
CPU热点
缓存行为
分支预测
调度延迟
锁竞争
内存分配

6.1 perf 性能分析

perf 是内置于 Linux 内核源码树中的性能剖析(profiling)工具,基于硬件性能计数器(PMU)和软件事件,能够以极低的开销对系统进行全栈性能分析。它位于内核源码树的 tools/perf 目录下,随内核版本一起发布。

6.1.1 perf 架构与子系统(perf 工具总览)

perf 工具集包含多个子命令,覆盖从数据采集到分析展示的完整流程:
#mermaid-svg-G9DOylQiNfxXNJM5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-G9DOylQiNfxXNJM5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-G9DOylQiNfxXNJM5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-G9DOylQiNfxXNJM5 .error-icon{fill:#552222;}#mermaid-svg-G9DOylQiNfxXNJM5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-G9DOylQiNfxXNJM5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-G9DOylQiNfxXNJM5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-G9DOylQiNfxXNJM5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-G9DOylQiNfxXNJM5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-G9DOylQiNfxXNJM5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-G9DOylQiNfxXNJM5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-G9DOylQiNfxXNJM5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-G9DOylQiNfxXNJM5 .marker.cross{stroke:#333333;}#mermaid-svg-G9DOylQiNfxXNJM5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-G9DOylQiNfxXNJM5 p{margin:0;}#mermaid-svg-G9DOylQiNfxXNJM5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-G9DOylQiNfxXNJM5 .cluster-label text{fill:#333;}#mermaid-svg-G9DOylQiNfxXNJM5 .cluster-label span{color:#333;}#mermaid-svg-G9DOylQiNfxXNJM5 .cluster-label span p{background-color:transparent;}#mermaid-svg-G9DOylQiNfxXNJM5 .label text,#mermaid-svg-G9DOylQiNfxXNJM5 span{fill:#333;color:#333;}#mermaid-svg-G9DOylQiNfxXNJM5 .node rect,#mermaid-svg-G9DOylQiNfxXNJM5 .node circle,#mermaid-svg-G9DOylQiNfxXNJM5 .node ellipse,#mermaid-svg-G9DOylQiNfxXNJM5 .node polygon,#mermaid-svg-G9DOylQiNfxXNJM5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-G9DOylQiNfxXNJM5 .rough-node .label text,#mermaid-svg-G9DOylQiNfxXNJM5 .node .label text,#mermaid-svg-G9DOylQiNfxXNJM5 .image-shape .label,#mermaid-svg-G9DOylQiNfxXNJM5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-G9DOylQiNfxXNJM5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-G9DOylQiNfxXNJM5 .rough-node .label,#mermaid-svg-G9DOylQiNfxXNJM5 .node .label,#mermaid-svg-G9DOylQiNfxXNJM5 .image-shape .label,#mermaid-svg-G9DOylQiNfxXNJM5 .icon-shape .label{text-align:center;}#mermaid-svg-G9DOylQiNfxXNJM5 .node.clickable{cursor:pointer;}#mermaid-svg-G9DOylQiNfxXNJM5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-G9DOylQiNfxXNJM5 .arrowheadPath{fill:#333333;}#mermaid-svg-G9DOylQiNfxXNJM5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-G9DOylQiNfxXNJM5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-G9DOylQiNfxXNJM5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-G9DOylQiNfxXNJM5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-G9DOylQiNfxXNJM5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-G9DOylQiNfxXNJM5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-G9DOylQiNfxXNJM5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-G9DOylQiNfxXNJM5 .cluster text{fill:#333;}#mermaid-svg-G9DOylQiNfxXNJM5 .cluster span{color:#333;}#mermaid-svg-G9DOylQiNfxXNJM5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-G9DOylQiNfxXNJM5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-G9DOylQiNfxXNJM5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-G9DOylQiNfxXNJM5 .icon-shape,#mermaid-svg-G9DOylQiNfxXNJM5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-G9DOylQiNfxXNJM5 .icon-shape p,#mermaid-svg-G9DOylQiNfxXNJM5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-G9DOylQiNfxXNJM5 .icon-shape .label rect,#mermaid-svg-G9DOylQiNfxXNJM5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-G9DOylQiNfxXNJM5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-G9DOylQiNfxXNJM5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-G9DOylQiNfxXNJM5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 动态与基准
子系统
分析
采集
perf 工具
perf record

采样记录
perf stat

计数器统计
perf top

实时监控
perf trace

系统调用跟踪
perf report

报告分析
perf script

脚本输出
perf annotate

源码级分析
perf sched

调度分析
perf lock

锁分析
perf kmem

内存分析
perf c2c

缓存一致性
perf probe

动态追踪点
perf bench

基准测试

perf 子命令功能总表:

子命令 功能说明 典型场景
perf stat 性能计数器统计(指令数、缓存命中率等) 快速评估程序整体性能特征
perf record 记录性能数据到文件中 采集性能样本供后续分析
perf report 分析 perf record 记录的数据 定位热点函数
perf top 实时显示 CPU 热点函数 快速定位当前系统热点
perf trace 类似 strace 的系统调用跟踪 分析系统调用开销
perf sched 调度器分析(延迟、唤醒延迟) 分析调度延迟问题
perf kmem 内核内存分配分析 分析内核内存使用
perf lock 锁竞争分析 定位锁瓶颈
perf c2c 缓存一致性分析(False Sharing 检测) 多线程缓存性能问题
perf bench 内核子系统基准测试 性能基准对比
perf probe 动态添加追踪点(kprobes/uprobes) 无需重编译的动态追踪
perf script 读取 perf.data 并显示追踪信息 生成火焰图的前置步骤
perf annotate 源码级性能分析 定位热点指令
权限配置

perf 默认需要 root 权限或调整 perf_event_paranoid 参数:

bash 复制代码
# 查看当前权限级别
cat /proc/sys/kernel/perf_event_paranoid

# 临时放开权限(-1=无限制,0=允许访问CPU事件,1=允许用户态事件,2=仅内核态)
sudo sysctl -w kernel.perf_event_paranoid=-1

# 永久配置(/etc/sysctl.conf)
kernel.perf_event_paranoid = -1

perf_event_paranoid 权限级别说明:

权限说明 适用场景
-1 无限制,所有用户可访问所有事件 开发/测试环境
0 允许访问 CPU 级别事件(不含 raw tracepoint) 性能分析
1 仅允许用户态事件(默认值) 普通用户分析自身程序
2 仅允许内核态事件 受限环境

6.1.2 perf stat(性能计数器统计)

perf stat 通过读取硬件性能计数器,统计程序运行期间的关键性能指标。它不采样,而是精确计数,开销极低。

基本用法
bash 复制代码
perf stat [options] command [args]
常用选项
选项 说明 示例
-e \ 指定要统计的事件 perf stat -e cycles,instructions ./program
-a 系统范围统计(所有CPU) perf stat -a sleep 5
-p \ 统计指定进程 perf stat -p 1234
-r \ 重复运行取平均值 perf stat -r 5 ./program
-B 显示大页统计 perf stat -B ./program
-d 显示详细缓存统计 perf stat -d ./program
-x \ 指定输出分隔符(便于脚本解析) perf stat -x, ./program
常用硬件事件
事件名 说明 分析意义
cycles CPU 周期数 反映程序执行时间
instructions 执行的指令数 反映程序工作量
cache-references 缓存引用次数 内存访问频率
cache-misses 缓存未命中次数 缓存效率
branches 分支指令数 控制流复杂度
branch-misses 分支预测失败次数 分支预测效率
L1-dcache-loads L1 数据缓存加载 L1 缓存行为
L1-dcache-load-misses L1 数据缓存加载未命中 L1 缓存效率
LLC-loads 最后一级缓存加载 LLC 缓存行为
LLC-load-misses 最后一级缓存加载未命中 内存带宽压力
dTLB-loads 数据 TLB 加载 TLB 行为
dTLB-load-misses 数据 TLB 未命中 页面局部性
page-faults 缺页异常次数 内存分配行为
context-switches 上下文切换次数 调度压力
cpu-migrations CPU 迁移次数 亲和性问题
实战示例与输出解读
bash 复制代码
$ perf stat ./myop 3 5

 Performance counter stats for './myop 3 5':

      1.234567 task-clock (msec)         #    0.876 CPUs utilized
             2      context-switches          #    0.002 M/sec
             0      cpu-migrations            #    0.000 K/sec
            56      page-faults               #    0.045 M/sec
     3,456,789      cycles                    #    2.800 GHz
     5,678,901      instructions              #    1.64  insn per cycle
     1,234,567      branches                  # 1000.000 M/sec
        12,345      branch-misses             #    1.00% of all branches

     0.001409234 seconds time elapsed

输出字段解读:

字段 说明
task-clock (msec) 任务占用的 CPU 时间(毫秒)
CPUs utilized 实际使用的 CPU 核数(task-clock / wall-clock)
context-switches 上下文切换次数,过多说明线程竞争激烈
cpu-migrations CPU 迁移次数,过多说明未绑定 CPU
page-faults 缺页异常次数,反映首次内存分配
cycles CPU 周期总数
instructions 执行的指令总数
insn per cycle IPC(每周期指令数),核心性能指标
branches 分支指令总数
branch-misses 分支预测失败次数及占比
seconds time elapsed 程序实际运行时间(墙钟时间)
关键指标解读
指标 计算方式 健康范围 异常处理
IPC(Instructions Per Cycle) instructions / cycles >1.0 为良好 <0.5 说明存在严重停顿(缓存未命中、分支预测失败等)
缓存命中率 1 - cache-misses/cache-references >95% 为良好 <90% 需要优化数据访问模式
分支预测成功率 1 - branch-misses/branches >95% 为良好 <90% 考虑优化分支逻辑或使用 __builtin_expect
查看可用事件列表
bash 复制代码
# 列出所有可用事件
perf list

# 按类别过滤
perf list hw            # 硬件事件
perf list sw            # 软件事件
perf list cache         # 缓存事件
perf list tracepoint    # 追踪点事件
实践建议
分析目标 推荐命令
快速评估程序性能 perf stat ./program
详细缓存分析 perf stat -d ./program(显示 L1/LLC/TLB 详细数据)
多次运行取平均 perf stat -r 10 ./program
分析运行中的服务 perf stat -p <PID> sleep 10
系统整体评估 perf stat -a sleep 10

6.1.3 perf record / perf report(采样记录与报告分析)

perf record 通过定时采样(默认基于 CPU 周期)记录性能数据到 perf.data 文件,perf report 对采样数据进行统计分析,找出热点函数。

perf record 基本用法
bash 复制代码
# 记录程序运行的性能数据
perf record [options] command [args]

# 记录运行中进程
perf record [options] -p <PID>

# 系统范围记录
perf record [options] -a
perf record 常用选项
选项 说明 示例
-F \ 采样频率(Hz),默认 4000 perf record -F 999 -g ./program
-g 记录调用图(call graph) 生成火焰图时必须
-p \ 指定要采样的进程 perf record -g -p 1234
-e \ 指定采样事件 perf record -e cache-misses ./program
-C \ 指定 CPU perf record -C 0,1 -a
-o \ 指定输出文件 perf record -o my.data ./program
-a 系统范围采样(所有 CPU) perf record -a sleep 10
--call-graph dwarf 使用 DWARF 信息进行栈回溯 帧指针被优化掉时使用
--call-graph fp 使用帧指针进行栈回溯(默认) 需要 -fno-omit-frame-pointer
-c \ 每 count 个事件采样一次 perf record -c 100000 ./program
perf report 基本用法
bash 复制代码
# 交互式查看报告
perf report

# 指定输入文件
perf report -i perf.data

# 按调用图显示
perf report -g

# 输出为文本格式
perf report --stdio
perf report 交互键
说明
Enter 展开/折叠调用图
+ 展开当前节点
- 折叠当前节点
a 反汇编当前函数(进入 annotate)
/ 搜索函数名
q 退出
完整实战示例
bash 复制代码
# 1. 编译(保留帧指针和调试信息)
gcc -O2 -g -fno-omit-frame-pointer -o program program.c

# 2. 记录性能数据(采样频率99Hz,记录调用图)
perf record -F 99 -g ./program

# 3. 查看报告
perf report

# 4. 记录运行中的服务进程(采样30秒)
perf record -F 99 -g -p $(pidof myserver) -- sleep 30

# 5. 仅关注缓存未命中事件
perf record -e cache-misses -g ./program
perf report 输出示例与解读
复制代码
# Overhead  Command   Shared Object       Symbol
# ........  ........  ..................  ..............................
    45.23%  program   program             [.] compute_hash
    23.11%  program   libc-2.31.so        [.] malloc
    12.45%  program   program             [.] process_data
     8.67%  program   [kernel.kallsyms]   [k] page_fault
     5.32%  program   program             [.] main
字段 说明
Overhead 该函数占总采样数的百分比,是定位热点的关键指标
Command 产生采样的进程名
Shared Object 函数所在的共享库或可执行文件
Symbol 函数符号名,[.] 表示用户态,[k] 表示内核态

6.1.4 perf top(实时热点监控)

perf top 实时显示系统中占用 CPU 最多的函数,类似于 top 命令,但针对的是函数级别。

基本用法
bash 复制代码
perf top [options]
常用选项
选项 说明 示例
-p \ 仅监控指定进程 perf top -p 1234
-C \ 仅监控指定 CPU perf top -C 0
-e \ 指定监控事件 perf top -e cache-misses
-F \ 采样频率 perf top -F 999
-g 显示调用图 perf top -g
-K 不显示内核函数 perf top -K
-U 不显示用户态函数 perf top -U
交互键
说明
h 显示帮助
q 退出
p 按进程过滤
d 设置刷新间隔(秒)
f 按函数名过滤
Enter 进入选中函数的 annotate 视图
a 反汇编当前函数
E 展开调用图
实战示例
bash 复制代码
# 实时查看系统热点
perf top

# 仅查看指定进程的热点
perf top -p $(pidof nginx)

# 查看缓存未命中的热点
perf top -e cache-misses

# 查看指定CPU的热点
perf top -C 2
perf top 与 perf record + perf report 的区别
对比项 perf top perf record + perf report
数据保存 不保存,实时显示 保存到 perf.data 文件
适用场景 快速查看当前热点 需要事后深入分析
交互性 实时刷新 静态分析
调用图 支持(-g 支持(-g
火焰图 不支持 支持(配合 perf script)

6.1.5 perf trace(系统调用跟踪)

perf trace 跟踪系统调用,功能类似 strace,但开销更低(基于 eBPF/ftrace 而非 ptrace)。

基本用法
bash 复制代码
# 跟踪程序的系统调用
perf trace ./program

# 跟踪运行中的进程
perf trace -p <PID>

# 系统范围跟踪
perf trace -a
常用选项
选项 说明 示例
-p \ 跟踪指定进程 perf trace -p 1234
-e \ 过滤指定系统调用 perf trace -e open,read,write
-s 显示系统调用统计摘要 perf trace -s ./program
-T 显示时间戳 perf trace -T ./program
-D \ 仅显示耗时超过指定毫秒的调用 perf trace -D 10 ./program
与 strace 的对比
特性 perf trace strace
实现机制 eBPF/ftrace ptrace
性能开销 低(~2-5%) 高(~50-100%)
输出详细度 中等 高(显示参数和返回值)
适用场景 生产环境快速诊断 开发环境详细调试
实战示例
bash 复制代码
# 统计程序的系统调用分布
$ perf trace -s ./myop 3 5

 Summary of events:

 myop (12345), 5 events, 100.0%

   syscall            calls    total       min       avg       max
   --------         --------  --------  --------  --------  --------
   write                   2    0.012 ms   0.005     0.006     0.007
   read                    1    0.008 ms   0.008     0.008     0.008
   brk                     1    0.003 ms   0.003     0.003     0.003
   exit_group              1    0.001 ms   0.001     0.001     0.001

6.1.6 perf sched(调度器分析)

perf sched 分析内核调度器行为,包括调度延迟、唤醒延迟、迁移等。

子命令
命令 说明
perf sched record 记录调度事件
perf sched latency 显示调度延迟(任务从唤醒到实际运行的延迟)
perf sched map 显示 CPU 任务映射(时间线上各 CPU 运行的任务)
perf sched trace 显示调度追踪详情
perf sched script 生成脚本输出
perf sched replay 重放调度事件
实战示例
bash 复制代码
# 1. 记录调度事件(10秒)
perf sched record -a -- sleep 10

# 2. 查看调度延迟
perf sched latency

# 3. 查看CPU任务映射
perf sched map
perf sched latency 输出解读
复制代码
 Task                  |  Runtime ms  |  Switches  |  Average delay ms  |  Maximum delay ms
-----------------------+--------------+------------+--------------------+------------------
 nginx:1234            |    1234.567  |     12345  |             0.234  |           12.345
 worker:5678           |     987.654  |      9876  |             0.123  |            5.678
字段 含义
Runtime ms 任务总运行时间
Switches 上下文切换次数
Average delay ms 任务从被唤醒到实际获得 CPU 的平均延迟
Maximum delay ms 最大调度延迟,用于发现偶发的调度抖动
perf sched map 输出解读

perf sched map 以时间线方式显示各 CPU 上运行的任务,每个字符代表一个任务,相同字符表示同一任务。可以直观看到任务在不同 CPU 之间的迁移情况。

易错点
问题 原因 解决方法
调度延迟数据为空 未使用 -a 全系统记录 确保使用 perf sched record -a
数据量过大 记录时间过长 缩短记录时间或过滤特定进程
Maximum delay 异常大 可能存在 CPU 被长时间占用 检查是否有 RT 任务或中断风暴

6.1.7 perf lock(锁竞争分析)

perf lock 分析锁竞争情况,帮助定位多线程程序中的锁瓶颈。

基本用法
bash 复制代码
# 1. 记录锁事件
perf lock record -a -- sleep 10

# 或记录指定进程
perf lock record -p <PID> -- sleep 10

# 2. 分析锁竞争报告
perf lock report
perf lock report 输出字段
字段 说明
Name 锁的名称
acquired 锁被获取的次数
contended 发生竞争的次数
avg wait 平均等待时间
max wait 最大等待时间
实战示例
bash 复制代码
# 记录多线程程序的锁事件
$ perf lock record -p $(pidof myserver) -- sleep 30

# 查看锁竞争报告
$ perf lock report

                Name   acquired  contended   avg wait (ns)   max wait (ns)
           &pool->lock    1250000      35000            1250           98000
         &cache->mutex     890000      12000             890           45000
         &queue->spin      560000       8000             650           32000
输出解读与分析要点
分析指标 判断标准 优化方向
contended / acquired 比值 >5% 说明竞争激烈 减小锁粒度或使用无锁结构
avg wait 过大 锁持有时间过长 缩短临界区代码
max wait 远大于 avg wait 存在偶发的严重竞争 检查是否有优先级反转
注意事项
  • perf lock 依赖内核的 CONFIG_LOCK_STAT 配置选项
  • 需要 root 权限运行
  • 对于用户态的 pthread 锁,需要内核支持 uprobes

6.1.8 perf c2c(伪共享检测)

perf c2c(Cache-to-Cache)检测 False Sharing(伪共享)问题。当多个线程频繁修改同一缓存行中的不同变量时,会导致缓存行在多个 CPU 之间反复失效和同步,严重影响性能。

基本用法
bash 复制代码
# 1. 记录缓存一致性事件
perf c2c record ./program

# 或系统范围
perf c2c record -a -- sleep 10

# 2. 生成报告
perf c2c report
False Sharing 的典型特征
  • 多个线程各自修改独立的变量,但这些变量恰好位于同一缓存行(通常 64 字节)
  • perf c2c report 会显示高 HITM(Hit Modified)率的内存地址
perf c2c report 输出关键字段
字段 说明
HITM Hit Modified,表示缓存行被其他 CPU 修改后再次访问的次数
Total Load 总加载次数
Store Refs 存储引用次数
Data Offset 数据在缓存行中的偏移
Code address 产生访问的代码地址
Symbol 对应的函数符号
解决方法

使用 __attribute__((aligned(64))) 或填充(padding)将变量对齐到不同缓存行。

c 复制代码
// 存在 False Sharing 的代码
struct {
    int counter_a;  // 线程0频繁写入
    int counter_b;  // 线程1频繁写入
} shared_data;      // counter_a 和 counter_b 可能在同一缓存行

// 修复:对齐到缓存行边界
struct {
    int counter_a __attribute__((aligned(64)));  // 独占一个缓存行
    int counter_b __attribute__((aligned(64)));  // 独占另一个缓存行
} shared_data;
实战示例
bash 复制代码
# 记录并分析
$ perf c2c record ./multithread_program
$ perf c2c report

# 查看高 HITM 率的地址
$ perf c2c report --sort=hitm

6.1.9 perf kmem(内核内存分配分析)

perf kmem 分析内核内存分配行为,跟踪 kmallockfreealloc_pages 等内核内存分配函数。

基本用法
bash 复制代码
# 1. 记录内核内存分配事件
perf kmem record -- sleep 10

# 2. 查看统计信息
perf kmem stat

# 按分配大小排序
perf kmem stat --sort bytes

# 按调用者排序
perf kmem stat --sort caller
常用选项
选项 说明 示例
--sort \ 排序方式(bytes/calls/frag/page/caller) perf kmem stat --sort bytes
--line \ 显示前 n 条记录 perf kmem stat --line 20
--alloc 仅统计分配 perf kmem stat --alloc
--free 仅统计释放 perf kmem stat --free
--caller 显示调用者信息 perf kmem stat --caller
--page 统计页面分配 perf kmem stat --page
perf kmem stat 输出示例
复制代码
SUMMARY
=======
Total bytes requested: 1048576
Total bytes wasted: 0
Total bytes allocated: 1048576
Net total bytes allocated: 1048576
Total bytes freed: 0
Net total bytes freed: 0

Callsite                | Alloc Bytes | Free Bytes | Frag
------------------------+-------------+------------+------
0xffffffff81234567      |        4096 |          0 |  0.0%
0xffffffff81345678      |        2048 |          0 |  0.0%
适用场景
场景 说明
内核内存泄漏 观察 Net total bytes 是否持续增长
内存碎片 查看 Frag 列,高碎片率说明分配效率低
热点分配者 --sort caller 找出最频繁的分配调用点

6.1.10 perf probe(动态追踪点)

perf probe 动态添加追踪点(基于 kprobes/uprobes),无需重新编译内核或应用程序即可追踪特定函数或代码位置。

基本用法
bash 复制代码
# 添加内核追踪点(追踪 do_sys_open 函数,记录 filename 参数)
perf probe -a 'do_sys_open filename:string'

# 添加用户空间追踪点(追踪 libc 的 malloc 函数,记录 size 参数)
perf probe -x /lib/x86_64-linux-gnu/libc.so.6 'malloc size'

# 列出已添加的追踪点
perf probe -l

# 删除追踪点
perf probe -d do_sys_open

# 删除所有追踪点
perf probe -d '*'

# 查看可探测的函数
perf probe -F
常用选项
选项 说明 示例
-a \ 添加追踪点 perf probe -a 'do_sys_open filename:string'
-d \ 删除追踪点 perf probe -d do_sys_open
-l 列出所有追踪点 perf probe -l
-F 列出可探测的函数 perf probe -F
-x \ 指定用户态二进制 perf probe -x /lib/libc.so.6 'malloc'
-L \ 查看函数的可探测行 perf probe -L do_sys_open
-V \ 查看函数可用的变量 perf probe -V do_sys_open
配合 perf record 使用
bash 复制代码
# 添加追踪点
perf probe -a 'do_sys_open filename:string'

# 使用该追踪点进行采样
perf record -e probe:do_sys_open -aR sleep 10

# 查看结果
perf script
实战示例:追踪特定函数的参数
bash 复制代码
# 追踪内核 tcp_sendmsg 函数,记录 sk 和 size 参数
$ perf probe -a 'tcp_sendmsg sk size'

# 追踪用户态程序的特定函数
$ perf probe -x ./myserver 'handle_request request_id'

# 查看追踪结果
$ perf record -e probe:tcp_sendmsg -a -- sleep 5
$ perf script
注意事项
  • 内核追踪点需要 root 权限
  • 用户态追踪点需要二进制文件包含调试信息(-g 编译)
  • 追踪点使用完毕后应及时删除,避免影响系统性能

6.1.11 perf script / perf annotate(脚本输出与源码级分析)

perf script

perf.data 中的原始采样数据以文本形式输出,是生成火焰图的前置步骤。

bash 复制代码
# 输出所有采样数据
perf script > perf.data.script

# 仅输出特定字段
perf script -f comm,pid,tid,time,ip,sym,dso

# 输出调用栈
perf script -g

perf script 常用选项:

选项 说明 示例
-f \ 指定输出字段 perf script -f comm,pid,tid,time,ip,sym
-g 输出调用栈 perf script -g
-i \ 指定输入文件 perf script -i my.data
-F \ 同 -f(旧版本) ---

perf script 输出示例:

复制代码
myop 12345 [002] 12345.678901: 250000 cycles:
        400b80 compute_hash (/home/user/program)
        400a20 process_data (/home/user/program)
        400950 main (/home/user/program)
  7f1234 __libc_start_main (/lib/x86_64-linux-gnu/libc-2.31.so)
perf annotate

对指定函数进行源码级(或汇编级)性能标注,显示每条指令的采样占比。

bash 复制代码
# 在 perf report 中按 'a' 键进入
# 或直接命令行使用
perf annotate <function_name>

# 指定输入文件
perf annotate -i perf.data compute_hash

perf annotate 输出示例:

复制代码
 Percent│      mov    %eax,%edx
   2.34│      add    $0x1,%edx
  45.67│      imul   %ecx,%edx    ← 热点指令
   1.23│      mov    %edx,(%rbx)

解读要点:Percent 列表示该指令被采样命中的占比。占比最高的指令即为函数内部的性能瓶颈。

6.1.12 perf bench(基准测试套件)

perf bench 提供内核子系统的基准测试套件,用于评估和对比系统性能。

子命令
命令 说明 示例
perf bench sched 调度器基准测试 perf bench sched messaging
perf bench mem 内存操作基准测试 perf bench mem memcpy
perf bench futex futex 基准测试 perf bench futex wake
perf bench epoll epoll 基准测试 perf bench epoll wait
实战示例
bash 复制代码
# 内存拷贝基准测试
$ perf bench mem memcpy
# Copying 1MB bytes ...
# 3.452 GB/sec

# 调度器消息传递基准测试
$ perf bench sched messaging
# Running 'sched/messaging' benchmark:
# 20 sender and receiver processes per group
# 10 groups == 400 processes run
Total time: 0.123 [sec]

# futex 唤醒基准测试
$ perf bench futex wake
# Waking 100 threads...
# 100 threads woken in 0.001234 sec
适用场景
场景 推荐命令
评估内存带宽 perf bench mem memcpy
评估调度器性能 perf bench sched messaging
评估锁唤醒性能 perf bench futex wake
评估 epoll 性能 perf bench epoll wait
对比不同内核版本 在两个内核上运行相同 bench 对比结果

6.1.13 火焰图生成流程

火焰图(Flame Graph)是一种直观展示函数调用栈和时间消耗的可视化工具。X 轴表示采样占比(越宽表示占用越多),Y 轴表示调用栈深度。
#mermaid-svg-zulk2xZzE8VFT3GV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zulk2xZzE8VFT3GV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zulk2xZzE8VFT3GV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zulk2xZzE8VFT3GV .error-icon{fill:#552222;}#mermaid-svg-zulk2xZzE8VFT3GV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zulk2xZzE8VFT3GV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zulk2xZzE8VFT3GV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zulk2xZzE8VFT3GV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zulk2xZzE8VFT3GV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zulk2xZzE8VFT3GV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zulk2xZzE8VFT3GV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zulk2xZzE8VFT3GV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zulk2xZzE8VFT3GV .marker.cross{stroke:#333333;}#mermaid-svg-zulk2xZzE8VFT3GV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zulk2xZzE8VFT3GV p{margin:0;}#mermaid-svg-zulk2xZzE8VFT3GV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zulk2xZzE8VFT3GV .cluster-label text{fill:#333;}#mermaid-svg-zulk2xZzE8VFT3GV .cluster-label span{color:#333;}#mermaid-svg-zulk2xZzE8VFT3GV .cluster-label span p{background-color:transparent;}#mermaid-svg-zulk2xZzE8VFT3GV .label text,#mermaid-svg-zulk2xZzE8VFT3GV span{fill:#333;color:#333;}#mermaid-svg-zulk2xZzE8VFT3GV .node rect,#mermaid-svg-zulk2xZzE8VFT3GV .node circle,#mermaid-svg-zulk2xZzE8VFT3GV .node ellipse,#mermaid-svg-zulk2xZzE8VFT3GV .node polygon,#mermaid-svg-zulk2xZzE8VFT3GV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zulk2xZzE8VFT3GV .rough-node .label text,#mermaid-svg-zulk2xZzE8VFT3GV .node .label text,#mermaid-svg-zulk2xZzE8VFT3GV .image-shape .label,#mermaid-svg-zulk2xZzE8VFT3GV .icon-shape .label{text-anchor:middle;}#mermaid-svg-zulk2xZzE8VFT3GV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zulk2xZzE8VFT3GV .rough-node .label,#mermaid-svg-zulk2xZzE8VFT3GV .node .label,#mermaid-svg-zulk2xZzE8VFT3GV .image-shape .label,#mermaid-svg-zulk2xZzE8VFT3GV .icon-shape .label{text-align:center;}#mermaid-svg-zulk2xZzE8VFT3GV .node.clickable{cursor:pointer;}#mermaid-svg-zulk2xZzE8VFT3GV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zulk2xZzE8VFT3GV .arrowheadPath{fill:#333333;}#mermaid-svg-zulk2xZzE8VFT3GV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zulk2xZzE8VFT3GV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zulk2xZzE8VFT3GV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zulk2xZzE8VFT3GV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zulk2xZzE8VFT3GV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zulk2xZzE8VFT3GV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zulk2xZzE8VFT3GV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zulk2xZzE8VFT3GV .cluster text{fill:#333;}#mermaid-svg-zulk2xZzE8VFT3GV .cluster span{color:#333;}#mermaid-svg-zulk2xZzE8VFT3GV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zulk2xZzE8VFT3GV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zulk2xZzE8VFT3GV rect.text{fill:none;stroke-width:0;}#mermaid-svg-zulk2xZzE8VFT3GV .icon-shape,#mermaid-svg-zulk2xZzE8VFT3GV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zulk2xZzE8VFT3GV .icon-shape p,#mermaid-svg-zulk2xZzE8VFT3GV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zulk2xZzE8VFT3GV .icon-shape .label rect,#mermaid-svg-zulk2xZzE8VFT3GV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zulk2xZzE8VFT3GV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zulk2xZzE8VFT3GV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zulk2xZzE8VFT3GV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} perf record

-F 99 -g
perf script

输出文本
stackcollapse-perf.pl

折叠调用栈
flamegraph.pl

生成SVG
flame.svg

浏览器打开

完整操作步骤
bash 复制代码
# 1. 记录性能数据(采样频率99Hz,记录调用图)
perf record -F 99 -g ./program

# 2. 生成脚本输出
perf script > out.perf

# 3. 下载 FlameGraph 工具
git clone https://github.com/brendangregg/FlameGraph.git

# 4. 折叠调用栈并生成火焰图
FlameGraph/stackcollapse-perf.pl out.perf | FlameGraph/flamegraph.pl > flame.svg

# 5. 用浏览器打开 flame.svg 查看
火焰图解读要点
特征 含义 行动
色块越宽 该函数占用CPU时间越多(热点) 优先优化
顶部宽平顶 该函数自身消耗大量CPU(非调用子函数) 分析该函数内部逻辑
深层嵌套 调用链较深,可能存在过度抽象 考虑减少调用层次
颜色 随机分配,无特殊含义 不要过度解读颜色
尖塔形状 某个调用路径占比大 分析该路径是否必要
底部宽 该调用路径被频繁执行 关注整体路径
差分火焰图(对比优化前后)
bash 复制代码
# 生成优化前的火焰图数据
perf record -F 99 -g -o before.data ./program_before
perf script -i before.data > before.perf

# 生成优化后的火焰图数据
perf record -F 99 -g -o after.data ./program_after
perf script -i after.data > after.perf

# 生成差分火焰图(红色=增加,蓝色=减少)
FlameGraph/stackcollapse-perf.pl before.perf > before.folded
FlameGraph/stackcollapse-perf.pl after.perf > after.folded
FlameGraph/difffolded.pl before.folded after.folded | FlameGraph/flamegraph.pl > diff.svg

6.2 gprof 性能剖析

gprof(GNU Profiler)是 GCC 自带的传统性能剖析工具,通过在编译时插入计数代码(-pg 选项),在程序运行时记录每个函数的调用次数和执行时间。与 perf 的采样方式不同,gprof 采用精确计数方式,每次函数调用都会被记录,因此结果完全精确(无统计误差),但代价是较大的运行时开销。

6.2.1 gprof 与 perf 的对比

特性 gprof perf
原理 编译时插桩(计数器) 硬件采样(PMU)
需要重编译 是(必须加 -pg
性能开销 较大(5-15%) 极小(<1%)
精度 精确(每次调用都记录) 统计性(采样)
内核函数 不支持 支持
多线程 有限支持 完全支持
缓存/分支分析 不支持 支持
可视化 文本输出 火焰图、hotspot
适用场景 小型程序精确分析 大型程序/生产环境

6.2.2 编译与运行(基本用法)

gprof 的使用流程分为三步:编译(插入计数代码)→ 运行(生成数据文件)→ 分析(解读报告)。

bash 复制代码
# 步骤1:编译(插入性能分析代码,必须同时加 -pg 到编译和链接阶段)
gcc -pg -g -o program program.c

# 步骤2:运行程序(会在当前目录生成 gmon.out 文件)
./program

# 步骤3:确认 gmon.out 已生成
ls -la gmon.out

# 步骤4:分析
gprof program gmon.out > analysis.txt
注意事项
注意点 说明
-pg 必须同时出现在编译和链接命令中 仅在编译阶段加 -pg 不够,链接时也需要
Makefile 中的配置 需要在 CFLAGSLDFLAGS 中都加入 -pg
程序必须正常退出 调用 exit() 或从 main 返回,gmon.out 才会生成
SIGKILL 终止 如果程序通过 SIGKILL 终止,gmon.out 不会生成
静态库 静态库也需要用 -pg 编译才能被 gprof 追踪
动态库 动态库中的函数默认不被追踪(除非库也用 -pg 编译)

6.2.3 gprof 命令分析(常用选项)

基本用法
bash 复制代码
gprof [options] program gmon.out
常用选项速查表
选项 说明 典型用途
-b 简要输出,去除冗余说明文字 脚本处理输出
-p 仅显示 Flat Profile 快速查看各函数耗时
-q 仅显示 Call Graph 分析调用关系
-z 显示未被调用的函数 发现死代码
-Z 不显示未被调用的函数 精简输出
-l 按源码行计数 定位热点代码行(需 -g -pg 编译)
-A 显示所有信息 完整分析
-n 显示每个函数的调用者和被调用者 调用链分析
-N 不显示调用者和被调用者 精简 Call Graph
-J 不显示注释 精简输出
-w \ 设置输出宽度 控制输出格式
常用命令组合
bash 复制代码
# 基本分析
gprof program gmon.out > analysis.txt

# 简要输出(去除冗余说明)
gprof -b program gmon.out

# 仅显示 flat profile
gprof -p program gmon.out

# 仅显示 call graph
gprof -q program gmon.out

# 显示未使用的函数(发现死代码)
gprof -z program gmon.out

# 不显示未使用的函数
gprof -Z program gmon.out

# 按行计数(需要 -g -pg 编译)
gprof -l program gmon.out

# 显示所有信息
gprof -A program gmon.out

6.2.4 实战示例(完整的编译→运行→分析流程)

以下示例展示一个完整的多文件项目使用 gprof 进行性能分析的全过程。

示例代码
c 复制代码
// main.c
#include <stdio.h>

extern void process_data(int n);

int main(void)
{
    process_data(1000);
    return 0;
}
c 复制代码
// compute.c
#include <math.h>

double compute_hash(int x)
{
    double result = 0.0;
    for (int i = 0; i < 1000; i++) {
        result += sin(x * i) * cos(x + i);
    }
    return result;
}

void process_data(int n)
{
    for (int i = 0; i < n; i++) {
        compute_hash(i);
    }
}
完整操作流程
bash 复制代码
# 1. 编译(-pg 必须同时出现在编译和链接中)
gcc -pg -g -O2 -o program main.c compute.c -lm

# 2. 运行程序(生成 gmon.out)
./program

# 3. 确认数据文件已生成
ls -la gmon.out
# -rw-r--r-- 1 user user 736 Jan 15 10:30 gmon.out

# 4. 分析(完整输出)
gprof program gmon.out

# 5. 简要输出(推荐,去除冗余说明文字)
gprof -b program gmon.out

# 6. 仅看 Flat Profile
gprof -b -p program gmon.out

# 7. 仅看 Call Graph
gprof -b -q program gmon.out

# 8. 按行计数(定位热点代码行)
gprof -b -l program gmon.out
多文件 Makefile 配置
makefile 复制代码
CC = gcc
CFLAGS = -pg -g -O2 -Wall
LDFLAGS = -pg -lm

SRCS = main.c compute.c
OBJS = $(SRCS:.c=.o)
TARGET = program

all: $(TARGET)

$(TARGET): $(OBJS)
	$(CC) $(OBJS) -o $@ $(LDFLAGS)

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

clean:
	rm -f $(OBJS) $(TARGET) gmon.out

profile: $(TARGET)
	./$(TARGET)
	gprof -b $(TARGET) gmon.out

.PHONY: all clean profile

6.2.5 输出解读

gprof 输出包含两部分:Flat Profile (平坦剖析)和 Call Graph(调用图)。

Flat Profile(平坦剖析)

Flat Profile 列出每个函数的执行时间统计,按耗时从高到低排序。

复制代码
Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 62.50      0.05     0.05     1000     0.05     0.05  compute_hash
 25.00      0.07     0.02      100     0.20     0.70  process_data
 12.50      0.08     0.01        1    10.00    80.00  main

Flat Profile 字段解读:

字段 说明
% time 该函数占总执行时间的百分比
cumulative seconds 累计执行时间(包含本函数及之前所有函数)
self seconds 该函数自身消耗的时间(不含被调用函数)
calls 该函数被调用的次数
self ms/call 每次调用自身消耗的平均时间
total ms/call 每次调用消耗的总平均时间(含被调用函数)
name 函数名
Call Graph(调用图)

Call Graph 展示函数之间的调用关系和时间传播。每个函数条目由三部分组成:调用者(上方缩进行)、函数本身(中间行)、被调用者(下方缩进行)。

复制代码
Call graph (explanation follows)

granularity: each sample print covers each 0.01 seconds.

index % time    self  children    called  name
                0.01    0.07       1/1           main [2]
[1]     87.5    0.01    0.07       1         process_data [1]
                0.05    0.00    1000/1000      compute_hash [3]
-----------------------------------------------
                0.01    0.07       1/1           __libc_start_main [4]
[2]    100.0    0.01    0.07       1         main [2]
                0.01    0.07       1/1           process_data [1]
-----------------------------------------------
                0.05    0.00    1000/1000      process_data [1]
[3]     62.5    0.05    0.00    1000         compute_hash [3]
-----------------------------------------------

Call Graph 字段解读:

字段 说明
index 函数编号(对应 Call Graph 中的 [N]
% time 该函数及其所有子函数占总时间的百分比
self 该函数自身消耗的时间(不含子函数)
children 该函数所有子函数消耗的时间总和
called 调用关系,格式为 本次调用次数/总调用次数,后面跟调用者名称
name 函数名
Call Graph 结构解读

以上述 process_data [1] 为例:

复制代码
                0.01    0.07       1/1           main [2]        ← 调用者:main 调用了 process_data 1次
[1]     87.5    0.01    0.07       1         process_data [1]    ← 函数本身:占总时间87.5%
                0.05    0.00    1000/1000      compute_hash [3]  ← 被调用者:process_data 调用了 compute_hash 1000次
Call Graph 特殊标记说明
标记 含义
<spontaneous> 表示该函数的调用者未知(如 main 由运行时启动,无明确调用者)
[N] 函数编号,用于交叉引用
上方缩进行 调用该函数的父函数(caller)
下方缩进行 该函数调用的子函数(callee)
递归调用 函数名后显示 [N] 编号与自身相同,表示递归
- 分隔线 分隔不同函数的条目
递归调用的 Call Graph 表示

当函数存在递归调用时,gprof 会在 called 列中显示递归信息:

复制代码
index % time    self  children    called  name
                0.03    0.02     500/1500     factorial [3]
[3]     62.5    0.03    0.00    1500+1000    factorial [3]
                0.02    0.00    1000/1500     factorial [3]
-----------------------------------------------

其中 1500+1000 表示:1500 次来自外部调用,另有 1000 次递归调用(自身调用自身)。

gprof 输出分析要点
分析目标 关注内容
找到最耗时函数 Flat Profile 中 % time 最高的函数
分析调用开销 对比 self ms/calltotal ms/call,差值大说明子函数开销大
发现过度调用 calls 列数值异常大的函数
优化调用链 Call Graph 中 children 值大的函数,优化其子函数
发现死代码 使用 gprof -z 查看从未被调用的函数

6.2.6 易错点与实践建议

易错点 说明 正确做法
只在编译时加 -pg 链接时也需要 -pg 编译和链接都加 -pg
程序被 kill -9 终止 gmon.out 不会生成 确保程序正常退出
优化级别过高 -O3 的内联优化会使函数"消失" 分析时加 -fno-inline
多线程程序 gprof 对多线程支持有限 多线程场景优先使用 perf
动态库函数不可见 默认不追踪动态库中的函数 静态链接或使用 perf
gmon.out 被覆盖 每次运行都会覆盖 分析前备份或重命名

6.3 性能分析编译配合选项回顾

性能分析工具的正确使用离不开编译选项的配合。以下是与性能分析直接相关的编译选项总结。

6.3.1 帧指针保留(-fno-omit-frame-pointer)

选项 说明
-fno-omit-frame-pointer 保留帧指针寄存器(RBP/FP),perf 和 gprof 需要用于调用栈回溯

默认情况下,-O2 及以上优化级别会省略帧指针以释放一个寄存器供通用使用。但这会导致 perf 无法正确回溯调用栈。

bash 复制代码
# 推荐:性能分析时使用
gcc -O2 -g -fno-omit-frame-pointer -o program program.c

注意: 如果无法重新编译(如分析第三方库),可使用 perf record --call-graph dwarf 通过 DWARF 调试信息进行栈回溯(需要 -g 编译)。

6.3.2 调试信息(-g)

选项 说明
-g 生成调试信息,perf report 和 perf annotate 需要用于显示源码行号
-g1 最小调试信息(仅回溯栈)
-g2 / -g 默认级别(符号表 + 行号)
-g3 包含宏定义等额外信息

6.3.3 禁用干扰优化

选项 说明 影响
-fno-inline 禁用函数内联 使每个函数独立可见,便于分析,但严重影响性能
-fno-optimize-sibling-calls 禁用尾调用优化 保持调用栈完整,防止尾调用被优化为跳转
实践建议
  • 初步定位问题时使用 -O2 -g -fno-omit-frame-pointer(保持优化但可分析)
  • 需要精确函数级分析时再加 -fno-inline
  • 生产环境性能分析不应禁用优化(会改变程序行为)

6.3.4 PGO 工作流(反馈优化)

PGO(Profile-Guided Optimization,反馈优化)利用程序实际运行的性能数据指导编译器优化,通常可带来 5-15% 的性能提升。
#mermaid-svg-NOp2xk3CsZECd53k{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NOp2xk3CsZECd53k .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NOp2xk3CsZECd53k .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NOp2xk3CsZECd53k .error-icon{fill:#552222;}#mermaid-svg-NOp2xk3CsZECd53k .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NOp2xk3CsZECd53k .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NOp2xk3CsZECd53k .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NOp2xk3CsZECd53k .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NOp2xk3CsZECd53k .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NOp2xk3CsZECd53k .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NOp2xk3CsZECd53k .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NOp2xk3CsZECd53k .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NOp2xk3CsZECd53k .marker.cross{stroke:#333333;}#mermaid-svg-NOp2xk3CsZECd53k svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NOp2xk3CsZECd53k p{margin:0;}#mermaid-svg-NOp2xk3CsZECd53k .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NOp2xk3CsZECd53k .cluster-label text{fill:#333;}#mermaid-svg-NOp2xk3CsZECd53k .cluster-label span{color:#333;}#mermaid-svg-NOp2xk3CsZECd53k .cluster-label span p{background-color:transparent;}#mermaid-svg-NOp2xk3CsZECd53k .label text,#mermaid-svg-NOp2xk3CsZECd53k span{fill:#333;color:#333;}#mermaid-svg-NOp2xk3CsZECd53k .node rect,#mermaid-svg-NOp2xk3CsZECd53k .node circle,#mermaid-svg-NOp2xk3CsZECd53k .node ellipse,#mermaid-svg-NOp2xk3CsZECd53k .node polygon,#mermaid-svg-NOp2xk3CsZECd53k .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NOp2xk3CsZECd53k .rough-node .label text,#mermaid-svg-NOp2xk3CsZECd53k .node .label text,#mermaid-svg-NOp2xk3CsZECd53k .image-shape .label,#mermaid-svg-NOp2xk3CsZECd53k .icon-shape .label{text-anchor:middle;}#mermaid-svg-NOp2xk3CsZECd53k .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NOp2xk3CsZECd53k .rough-node .label,#mermaid-svg-NOp2xk3CsZECd53k .node .label,#mermaid-svg-NOp2xk3CsZECd53k .image-shape .label,#mermaid-svg-NOp2xk3CsZECd53k .icon-shape .label{text-align:center;}#mermaid-svg-NOp2xk3CsZECd53k .node.clickable{cursor:pointer;}#mermaid-svg-NOp2xk3CsZECd53k .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NOp2xk3CsZECd53k .arrowheadPath{fill:#333333;}#mermaid-svg-NOp2xk3CsZECd53k .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NOp2xk3CsZECd53k .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NOp2xk3CsZECd53k .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NOp2xk3CsZECd53k .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NOp2xk3CsZECd53k .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NOp2xk3CsZECd53k .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NOp2xk3CsZECd53k .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NOp2xk3CsZECd53k .cluster text{fill:#333;}#mermaid-svg-NOp2xk3CsZECd53k .cluster span{color:#333;}#mermaid-svg-NOp2xk3CsZECd53k div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NOp2xk3CsZECd53k .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NOp2xk3CsZECd53k rect.text{fill:none;stroke-width:0;}#mermaid-svg-NOp2xk3CsZECd53k .icon-shape,#mermaid-svg-NOp2xk3CsZECd53k .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NOp2xk3CsZECd53k .icon-shape p,#mermaid-svg-NOp2xk3CsZECd53k .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NOp2xk3CsZECd53k .icon-shape .label rect,#mermaid-svg-NOp2xk3CsZECd53k .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NOp2xk3CsZECd53k .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NOp2xk3CsZECd53k .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NOp2xk3CsZECd53k :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 阶段1:插桩编译

gcc -fprofile-generate
运行程序

./program(收集数据)
生成 .gcda 文件

(性能计数数据)
阶段2:优化编译

gcc -fprofile-use
生成优化后的程序

(基于真实数据优化)

完整操作步骤
bash 复制代码
# 阶段1:插桩编译(生成收集性能数据的代码)
gcc -fprofile-generate -O2 -o program program.c

# 运行程序(使用真实或代表性的输入,生成 .gcda 文件)
./program input_data

# 阶段2:使用收集的数据进行优化编译
gcc -fprofile-use -O2 -o program program.c

# 如果数据不完整或有冲突,使用校正选项
gcc -fprofile-use -fprofile-correction -O2 -o program program.c
PGO 相关编译选项汇总
选项 说明
-fprofile-generate 插入代码收集性能数据(编译和链接都需要)
-fprofile-use 使用收集的性能数据进行优化(编译和链接都需要)
-fprofile-correction 数据校正(处理多线程导致的计数不一致)
-fauto-profile 使用外部性能分析数据(.afdo 文件,来自 perf)
-fprofile-generate=\ 指定 .gcda 文件输出目录
-fprofile-use=\ 指定 .gcda 文件读取目录
auto-profile 工作流(使用 perf 数据指导优化)
bash 复制代码
# 1. 使用 perf 采集数据
perf record -b ./program

# 2. 使用 create_gcov 工具转换(需要 autofdo 工具)
create_gcov --binary=./program --profile=perf.data --gcov=profile.afdo

# 3. 使用 afdo 文件优化编译
gcc -O2 -fauto-profile=profile.afdo -o program program.c

6.3.5 函数仪器化(-finstrument-functions)

选项 说明
-finstrument-functions 在每个函数入口和出口插入钩子调用
-finstrument-functions-exclude-file-list=\ 排除特定文件
-finstrument-functions-exclude-function-list=\ 排除特定函数

使用 -finstrument-functions 时,需要实现以下两个钩子函数:

c 复制代码
void __cyg_profile_func_enter(void *func, void *caller);
void __cyg_profile_func_exit(void *func, void *caller);

这两个函数会在每个被仪器化函数的入口和出口被自动调用,参数分别是当前函数地址和调用者地址。可配合 addr2line 将地址转换为函数名,实现自定义的性能追踪。

6.3.6 性能分析编译选项总结表

选项 用途 配合工具
-pg 插入 gprof 计数代码 gprof
-fno-omit-frame-pointer 保留帧指针,支持栈回溯 perf / gprof
-g 生成调试信息(行号、符号) perf report / perf annotate
-fno-inline 禁用内联,使函数可见 perf / gprof(调试阶段)
-fno-optimize-sibling-calls 禁用尾调用优化,保持调用栈 perf / gprof
-finstrument-functions 函数入口/出口插桩 自定义追踪工具
-fprofile-generate PGO 第一阶段:收集数据 PGO
-fprofile-use PGO 第二阶段:使用数据优化 PGO
-fauto-profile 使用 perf 数据进行 PGO autofdo
-fdebug-prefix-map==\ 标准化调试路径 使性能数据可重现
-gsplit-dwarf 分离调试信息到 .dwo 文件 减小二进制大小

6.4 性能分析工具对比总结

本章介绍了 Linux 下两大类性能分析工具(perf 和 gprof)以及 PGO 反馈优化机制。以下从多个维度进行对比总结。

6.4.1 工具对比总表

对比维度 perf gprof PGO
工具类型 性能分析工具 性能剖析工具 编译优化机制
工作原理 硬件采样(PMU) 编译时插桩(计数器) 编译时插桩 + 反馈优化
需要重编译 否(建议加 -g -fno-omit-frame-pointer 是(必须加 -pg 是(两阶段编译)
性能开销 极小(<1%) 较大(5-15%) 第一阶段有开销,最终产物无开销
精度 统计性(采样) 精确(每次调用都记录) N/A(不是分析工具)
分析粒度 函数级 + 指令级(annotate) 函数级 + 行级(-l N/A
内核函数 支持 不支持 不支持
多线程 完全支持 有限支持 支持(需 -fprofile-correction
缓存分析 支持(cache-misses 等事件) 不支持 间接优化(基于热点路径)
分支预测 支持(branch-misses) 不支持 间接优化(基于分支概率)
锁分析 支持(perf lock) 不支持 不支持
调度分析 支持(perf sched) 不支持 不支持
可视化 火焰图、hotspot 文本输出 N/A
适用场景 生产环境、大型程序、系统级分析 开发环境、小型程序、精确函数分析 发布版本性能提升
使用难度 中等 简单 中等
典型性能提升 N/A(分析工具) N/A(分析工具) 5-15%

6.4.2 工具选择决策图

#mermaid-svg-mHc13nhlwSb1ZkEm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mHc13nhlwSb1ZkEm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mHc13nhlwSb1ZkEm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mHc13nhlwSb1ZkEm .error-icon{fill:#552222;}#mermaid-svg-mHc13nhlwSb1ZkEm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mHc13nhlwSb1ZkEm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mHc13nhlwSb1ZkEm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mHc13nhlwSb1ZkEm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mHc13nhlwSb1ZkEm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mHc13nhlwSb1ZkEm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mHc13nhlwSb1ZkEm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mHc13nhlwSb1ZkEm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mHc13nhlwSb1ZkEm .marker.cross{stroke:#333333;}#mermaid-svg-mHc13nhlwSb1ZkEm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mHc13nhlwSb1ZkEm p{margin:0;}#mermaid-svg-mHc13nhlwSb1ZkEm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mHc13nhlwSb1ZkEm .cluster-label text{fill:#333;}#mermaid-svg-mHc13nhlwSb1ZkEm .cluster-label span{color:#333;}#mermaid-svg-mHc13nhlwSb1ZkEm .cluster-label span p{background-color:transparent;}#mermaid-svg-mHc13nhlwSb1ZkEm .label text,#mermaid-svg-mHc13nhlwSb1ZkEm span{fill:#333;color:#333;}#mermaid-svg-mHc13nhlwSb1ZkEm .node rect,#mermaid-svg-mHc13nhlwSb1ZkEm .node circle,#mermaid-svg-mHc13nhlwSb1ZkEm .node ellipse,#mermaid-svg-mHc13nhlwSb1ZkEm .node polygon,#mermaid-svg-mHc13nhlwSb1ZkEm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mHc13nhlwSb1ZkEm .rough-node .label text,#mermaid-svg-mHc13nhlwSb1ZkEm .node .label text,#mermaid-svg-mHc13nhlwSb1ZkEm .image-shape .label,#mermaid-svg-mHc13nhlwSb1ZkEm .icon-shape .label{text-anchor:middle;}#mermaid-svg-mHc13nhlwSb1ZkEm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mHc13nhlwSb1ZkEm .rough-node .label,#mermaid-svg-mHc13nhlwSb1ZkEm .node .label,#mermaid-svg-mHc13nhlwSb1ZkEm .image-shape .label,#mermaid-svg-mHc13nhlwSb1ZkEm .icon-shape .label{text-align:center;}#mermaid-svg-mHc13nhlwSb1ZkEm .node.clickable{cursor:pointer;}#mermaid-svg-mHc13nhlwSb1ZkEm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mHc13nhlwSb1ZkEm .arrowheadPath{fill:#333333;}#mermaid-svg-mHc13nhlwSb1ZkEm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mHc13nhlwSb1ZkEm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mHc13nhlwSb1ZkEm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mHc13nhlwSb1ZkEm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mHc13nhlwSb1ZkEm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mHc13nhlwSb1ZkEm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mHc13nhlwSb1ZkEm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mHc13nhlwSb1ZkEm .cluster text{fill:#333;}#mermaid-svg-mHc13nhlwSb1ZkEm .cluster span{color:#333;}#mermaid-svg-mHc13nhlwSb1ZkEm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mHc13nhlwSb1ZkEm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mHc13nhlwSb1ZkEm rect.text{fill:none;stroke-width:0;}#mermaid-svg-mHc13nhlwSb1ZkEm .icon-shape,#mermaid-svg-mHc13nhlwSb1ZkEm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mHc13nhlwSb1ZkEm .icon-shape p,#mermaid-svg-mHc13nhlwSb1ZkEm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mHc13nhlwSb1ZkEm .icon-shape .label rect,#mermaid-svg-mHc13nhlwSb1ZkEm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mHc13nhlwSb1ZkEm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mHc13nhlwSb1ZkEm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mHc13nhlwSb1ZkEm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} CPU热点定位

不能
小型/精确分析
大型/生产环境
缓存性能
分支预测
调度延迟
锁竞争
False Sharing
整体优化
性能问题
问题类型?
能否重编译?
程序规模?
perf record + perf report
gprof -pg
perf record -g + 火焰图
perf stat -e cache-misses
perf record -e cache-misses
perf stat -e branch-misses
perf sched record + latency
perf lock record + report
perf c2c record + report
PGO: -fprofile-generate → -fprofile-use

6.4.3 性能分析工作流推荐

阶段 推荐工具 目的
初步评估 perf stat 获取 IPC、缓存命中率等宏观指标
热点定位 perf record + perf report 找出 CPU 占用最高的函数
调用链分析 perf record -g + 火焰图 理解热点函数的调用路径
指令级分析 perf annotate 定位函数内部的热点指令
精确函数分析 gprof(小项目) 获取精确的调用次数和耗时
缓存优化 perf stat -d / perf record -e cache-misses 分析缓存行为
最终优化 PGO 基于真实负载数据优化编译

6.4.4 perf 子命令速查总表

子命令 功能 典型命令 适用场景
perf stat 计数器统计 perf stat -e cycles,instructions ./prog 快速评估
perf record 采样记录 perf record -F 99 -g ./prog 采集数据
perf report 报告分析 perf report 热点定位
perf top 实时监控 perf top -p PID 快速查看
perf trace 系统调用跟踪 perf trace -s ./prog 系统调用分析
perf sched 调度分析 perf sched latency 调度延迟
perf lock 锁分析 perf lock report 锁竞争
perf c2c 缓存一致性 perf c2c report False Sharing
perf kmem 内核内存 perf kmem stat 内核内存分配
perf probe 动态追踪点 perf probe -a 'func arg' 动态追踪
perf script 脚本输出 perf script > out.perf 火焰图前置
perf annotate 源码级分析 perf annotate func 指令级热点
perf bench 基准测试 perf bench mem memcpy 性能基准

6.4.5 gprof 选项速查总表

选项 功能 典型命令 适用场景
(无选项) 完整输出 gprog prog gmon.out 全面分析
-b 简要输出 gprof -b prog gmon.out 脚本处理
-p 仅 Flat Profile gprof -p prog gmon.out 快速看耗时
-q 仅 Call Graph gprof -q prog gmon.out 分析调用关系
-z 显示未调用函数 gprof -z prog gmon.out 发现死代码
-l 按行计数 gprof -l prog gmon.out 定位热点行

6.4.6 本章小结

本章系统介绍了 Linux 性能分析的完整工具链。perf 作为内核级性能分析工具,覆盖了从 CPU 热点到缓存行为、从调度延迟到锁竞争的全方位分析能力;gprof 作为传统的函数级剖析工具,在小型程序的精确分析中仍有不可替代的价值;PGO 则将性能分析数据反哺到编译优化中,实现"分析→优化"的闭环。三者的配合使用,构成了从"发现问题"到"解决问题"再到"预防问题"的完整性能工程体系。

相关推荐
躺不平的理查德2 小时前
嵌入式 Linux 系统移植与 SD 卡量产镜像制作
linux·运维·服务器
一直走下去-明3 小时前
centos7无法安装tcpreplay
linux·运维
吹什么轩3 小时前
linux网络:TCP套接字基础
linux·网络·tcp/ip
时空无限4 小时前
ubuntu dpkg -l 输出第一列解释
linux·运维·ubuntu
叮咚侠6 小时前
Ubuntu 24.04 安装zabbix-agent 5.0 服务
linux·ubuntu·zabbix·zabbix-agent
左手厨刀右手茼蒿8 小时前
数据仓库的架构演进:从MySQL到ClickHouse到数据湖的工程化实践
linux·嵌入式·系统内核
左手厨刀右手茼蒿8 小时前
创业团队技术选型:别用大厂架构解决小团队问题
linux·嵌入式·系统内核
bosins9 小时前
将 Hyper-V 上的 Ubuntu 虚拟机内容迁移到 WSL (Windows Subsystem for Linux) 中
linux·windows·ubuntu
我星期八休息10 小时前
扩展—DNS与ICMP
linux·服务器·网络