性能优化工具总结
性能优化通常结合多个维度、多种工具共同进行分析,下面对常用工具做一些简单的介绍。
1 虚拟文件系统
虚拟文件系统(Virtual File System, 简称 VFS) 是操作系统内核中的一个软件抽象层。procfs、sysfs 和 debugfs 都是基于虚拟文件系统(VFS)接口实现的“伪”文件系统(Pseudo Filesystems),它们直接映射内核内存数据,是内核向用户空间开放的实时探针。
1.1 procfs
/proc 用于提供与系统内核、进程、内存、系统资源等相关的实时信息。
它由内核动态生成,反映了当前系统的运行状态,可以通过读取 /proc 下的文件了解系统的实时信息,也可以通过文件写入数据,改变某些内核或系统的行为。
常见使用的内容有:
① /proc/cpuinfo:有关 CPU 的详细信息
② /proc/meminfo:提供系统内存的使用情况
③ /proc/interrupts:显示系统各个硬件中断的计数信息
④ /proc/[pid]/:进程的详细信息,每个目录对应一个正在运行的进程
⑤ /proc/[pid]/task/[tid]/:线程的详细信息
⑥ /proc/partitions:磁盘分区以及其大小
⑦ /proc/net:包含网络相关信息,如网络设备的状态与统计信息;TCP/UDP 连接信息等
1.2 sysfs
/sys 是 Linux 系统中 sysfs 虚拟文件系统的挂载点,同样用于暴露内核的各种信息和控制接口。但与 procfs 不同的是,它更多关注硬件设备和内核模块的状态、配置和控制,而 procfs 主要暴露进程和内核信息。
目录结构如下:
① /sys/devices:包含物理设备硬件信息,如 CPU、内存、PCI 设备等
② /sys/block:提供块设备(如硬盘、SSD)等信息
③ /sys/mouule:列出系统加载的内核模块
④ /sys/kernel:包含内核版本、调度器等各种内核信息与配置
⑤ /sys/class:该目录下文件夹代表系统中不同类型的设备,如网络设备/net,块设备/block 等
示例:
# 每次从主存读取到缓存的最小数据量
root@vm-node:/# cat coherency_line_size
64
>
# 通常为 L1 缓存
root@vm-node3:/# cat /sys/devices/system/cpu/cpu1/cache/index0/size
32K
1.3 debugfs
它是一个内核调试功能,用于帮助开发人员和系统管理员调试内核。它允许用户通过文件接口访问和内核的内部状态、变量、数据结构等信息,而从诊断和调试内核或驱动程序中的问题。
# 使用前进行挂载
mount -t debugfs none /sys/kernel/debug
# 查看内存状态
cat /sys/kernel/debug/lockdep/current_deadlocks
# 开启控制调度器追踪
echo 1 > /sys/kernel/debug/tracing/events/sched/enable
除去示例以外,它拥有更多的能力可以去发掘,这里不进行展开。
2 PERF 工具
基于 采样(Sampling) 机制,虽然高频采样会带来一定的 CPU 负载和采样偏移(Sampling Bias),但其对系统的侵入性极低,是生产环境的首选。
核心采样原理
在使用 perf record 时,有两个关键参数控制采样行为:
-
-F (Frequency):指定每秒采样次数。
例如:
-F 99表示每秒采样 99 次(为了避免与系统定时器同步产生偏倚,通常选择质数)。 -
-c (Count):指定每经过 $N$ 个事件采样一次。
若 CPU 为 2 GHz,想每 2 秒采集一次,则
-c 4000000000。
堆栈回溯机制:FP vs. DWARF
在 perf record -g 时,底层通过什么方式还原调用栈至关重要:
- Frame Pointer (FP):依靠寄存器(如 x86 的 RBP)指向栈基址。
- 性能极高,解析速度快,内核支持好
- 编译器默认开启
-fomit-frame-pointer优化,导致 FP 缺失,栈信息断裂
- DWARF:依靠编译时生成的
.debug_frame或.eh_frame调试信息。- 准确度最高,即使编译器开启了优化也能还原
- 开销大,采样时需要拷贝大量原始栈数据,会显著增加磁盘 I/O 负载,后期解析慢
若程序能够重新编译,推荐开启
-fno-omit-frame-pointer编译选项;反之使用--call-graph dwarf进行抓取
还有一些其他的采样方式,如:x86_64 内核引入 ORC (Oops Rewind Capability)、 Intel 处理器的硬件特性 LBR (Last Branch Record)等。
2.1 火焰图工具
火焰图直观地展示了 CPU 时间的消耗分布。宽度表示采样总时长(占比),高度表示调用栈深度。
# 采集数据 (包含调用栈 -g)
perf record -F 99 -a -g -- sleep 60
# 解析数据
perf script > out.perf
# 转换为折叠图
./FlameGraph/stackcollapse-perf.pl out.perf > out.folded
# 转折叠图为火焰图
./FlameGraph/flamegraph.pl out.folded > perf.svg
2.2 交互图工具
该项目能将多种性能剖析器的输出转换成 Graphviz 的 dot 图形语言格式,适用于查看函数之间的精确调用关系路径。
# 取 perf 采集数据 out.perf,生成(采集时需要 -g 参数)
python ./gprof2dot.py -f perf out.perf | dot -Tpng -o output.png
2.3 采样事件全景
通过 perf list 可以查看系统支持的所有事件,它们决定了“观察问题的视角”,总体可以将它分为三类:
1. Hardware Events (硬件事件)
由 CPU 内核中的 PMU (Performance Monitoring Unit) 硬件产生。它记录的是微观层面的处理器行为。
-
常见事件:
cycles(时钟周期)、instructions(指令数)、cache-references/misses(缓存访问/缺失)。 -
应用场景:用于计算每时钟周期指令数(IPC),衡量 CPU 的执行效率。当 IPC 远低于处理器理论上限时,通常意味着后端受限于访存延迟(Memory Bound)或前端受限于指令解码 。
-
典型示例:分支预测 (Branch Prediction) 失效
当代码中存在
if-else或循环时,CPU 会通过 BTB (Branch Target Buffer) 预测下一条指令。然而 BTB 容量有限,若循环体极大或嵌套过深,会导致 BTB 溢出,旧的预测记录被覆盖,造成预测失败(Branch-misses)。预测失败会导致 CPU 流水线冲刷(Flush),浪费大量时钟周期,开发者可通过
__builtin_expect或 C++20 的[[likely]]属性优化编译器生成的汇编排列,从而提升执行效率。
2. Software Events (软件事件)
由 Linux 内核维护的虚拟计数器,记录了操作系统层面的资源调度和异常。
-
常见事件:
context-switches(上下文切换)、cpu-migrations(进程跨核迁移)、page-faults(缺页中断)。 -
应用场景:用于诊断系统级资源竞争。例如,若
context-switches在无显著 I/O 压力时激增,往往预示着大量线程在争夺有限的互斥锁或 CPU 时间片。 -
典型示例:缺页中断调优
大量的
page-faults(尤其是 Major Faults)意味着系统频繁从磁盘加载代码或数据到物理内存。通过在程序启动前使用madvise(MADV_WILLNEED)或mlockall(),可以提前建立页表映射,减少运行时的长尾延迟 。
3. Tracepoint Events (静态追踪点)
追踪点是内核开发者手动埋入源码的“钩子”,代表了 Linux 内核的一种稳定二进制接口(ABI)。
-
核心语义:追踪点不仅记录事件发生,还携带丰富的结构化参数。例如
sched:sched_switch记录了被切出(Prev)和切入(Next)的任务信息。 -
应用场景:由于其高稳定性,追踪点是生产环境构建长期性能基准和安全审计的首选。
-
路径探索:所有可用的追踪点均通过
/sys/kernel/debug/tracing/events/暴露。每个追踪点都有一个format文件,详细描述了其记录的字段和偏移,供perf或bpftrace动态解析。 -
典型示例:磁盘 I/O 深度分析
通过
block:block_rq_issue(发起请求)和block:block_rq_complete(完成请求)两个追踪点,可以精准计算出每个 I/O 请求在驱动层的处理时长,排除文件系统逻辑层面的干扰。
2.3.1 动态插桩
当 perf list 中的预定义事件不足以深入业务逻辑时,动态插桩(Dynamic Probing)提供了即时修改运行中程序的能力 。
- Kprobes (Kernel): 允许在几乎任何内核指令处插入探测点,追踪内核函数。
- Uprobes (User):追踪用户态二进制文件或库(如
libc.so)。
实战示例:追踪用户态函数调用
假设需要监控某自定义应用 ./my_app 中函数 my_custom_func 被调用的次数和耗时:
- 添加插桩点:
perf probe -x ./my_app my_custom_func - 记录数据:
perf record -e probe_my_app:my_custom_func -a -g -- sleep 10 - 查看结果:
perf report
实现原理:
perf会在目标函数的入口处插入一条断点指令(如 x86 的int3)。当执行到此时,CPU 陷入异常处理,转而执行perf定义的探测函数(Handler),记录堆栈和寄存器信息,完成后恢复原指令执行。
2.4 多维专项分析
实际生产中,程序缓慢并不总是 CPU 的原因,此时需要切换观察维度。
2.4.1 调度分析 (Scheduling)
目标:解决为什么 进程排队等待CPU 或 CPU切换频繁 的问题,追踪任务状态切换事件。
核心命令:
# 记录调度器行为
perf sched record -- sleep 10
# 分析延迟:查看哪个进程被唤醒后等待最久才上 CPU (Max Delay)
perf sched latency
示例分析:通过 perf sched latency 发现 mysqld 的 Max Delay 很大,说明系统当前 CPU 压力过大,数据库进程长时间在就绪队列中等待,导致响应慢。
2.4.2 内存分析 (Memory)
目标:发现内存分配热点或潜在的泄漏,追踪分配器(kmalloc/malloc)调用。可结合火焰图进行可视化分析。
采样物理内存分配 (Page Faults):
perf record -e page-faults -g -- ./my_app
采样用户态分配 (Malloc):通过 Uprobe 监控 malloc 的调用。
perf probe -x /lib64/libc.so.6 malloc
perf record -e probe_libc:malloc -g -- ./my_app
示例分析:如果火焰图中某个业务函数占用了极宽的比例,说明该路径正在产生大量碎片化的小内存申请,建议改用内存池优化。
2.5 离线分析
生产环境中,我们通常只采集数据(生成 perf.data),然后将其拷贝到本地开发机进行详尽分析,以减少对生产业务的干扰。
离线分析的核心:符号还原
perf.data 记录的仅仅是二进制地址。要看到函数名,必须有符号表(Symbol Table)。
准备工作
-
kallsyms:内核符号表。在目标机执行
cat /proc/kallsyms > kallsyms.txt。 -
symfs (Symbol Root):符号文件根目录。将目标机上的二进制文件(可执行程序、
.so动态库、内核模块)按照原始路径拷贝到本地的一个文件夹中(如/tmp/target_root/)。
跨设备解析流程
# 拷贝 perf.data 到分析机器,分析机上执行
perf script -i perf.data --symfs=/path/to/symfs --kallsyms=/pth/tokallsyms.txt > out.perf
https://zhuanlan.zhihu.com/p/429827370
3 sys stat 工具包
由 Sebastien Godard 长期维护,提供一组用于 监控 Linux 系统性能和资源使用情况 的命令行工具。
3.1 iostat
主要用于监控系统设备的 CPU 使用率和 磁盘 I/O 状态。
命令格式: iostat [选项] [时间间隔] [报告次数]
- 示例:
iostat -dx 2 5(每 2 秒报告一次扩展统计,共 5 次)。 - 注意: 第一次报告的数据是自系统启动以来的累计平均值,从第二次开始才是该时间段内的即时值。
3.1.1 常用选项说明
| 选项 | 说明 | 补充 |
|---|---|---|
| -c | 仅显示 CPU 利用率报告 | 用于排查是否为 CPU 瓶颈 |
| -d | 仅显示磁盘利用率报告 | 屏蔽 CPU 信息,聚焦磁盘 |
| -x | 显示扩展统计信息 | 最常用,包含 await, %util 等核心指标 |
| -k / -m | 以 KB / MB 为单位显示 | 默认可能是块(block)单位,建议固定使用 -k |
| -p [device] | 指定设备及分区 | 如 -p sda 显示 sda 及其所有分区的详细数据 |
| -n | 显示 NFS 报告 | 监控网络文件系统的 I/O 状态 |
3.1.2 核心输出字段解析(-x 扩展)
A. 吞吐量指标 (Throughput)
-
r/s & w/s:每秒完成的读/写请求数(IOPS)。
-
rkB/s & wkB/s:每秒读/写的 KB 数(吞吐量)。
-
rrqm/s & wrqm/s:每秒合并(merge)的读/写请求数。合并越高说明连续 I/O 越多,性能越好。
B. 延迟与负载指标 (Latency & Load)
-
await:平均响应时间。包括在队列中的等待时间 + 实际服务时间。
- 标准: SSD 通常 < 1ms;机械硬盘持续 > 10ms 需要警惕。
-
avgqu-sz:平均请求队列长度。
- 标准: 理想值应小于磁盘数。若持续 > 2,说明磁盘存在处理瓶颈。
-
avgrq-sz:平均每个 I/O 请求的数据大小(扇区为单位)。
- 计算:
(rkB/s + wkB/s) * 2 / (r/s + w/s)。
可根据 avgrq-sz 判断业务类型,若:
< 32 (16KB):随机存取(数据库、邮件服务器)。此时关注 IOPS (tps) 和 await。
> 32 (16KB): 典型顺序存取(视频流、大文件备份)。此时关注 吞吐量 (kB/s)。
- 计算:
C. 利用率 (Utilization)
- %util:磁盘忙碌百分比。单位时间内有 I/O 请求的时间占比。
- 注意: 对于 SSD 或高速磁盘阵列,即使 %util 达到 100%,也并不代表带宽已满,因为它们具备极强的并发处理能力。