系统性能分析-CPU耗时定位
上篇博客介绍了 CPU异常分析的工具和方法。本文将介绍如何定位 CPU 耗时。
定位思路
mindmap
root((耗时分析))
system耗时高
可能原因
并发
网络IO
磁盘IO
内存分配
常用工具
dstat
vmstat
iostat
netstat
ifstat
user耗时高
耗时进程/线程定位
TOP
代码分析
strace
perf代码分析
strace
strace是一个可用于诊断、调试和教学的Linux用户空间跟踪器。我们用它来监控用户空间进程和内核的交互,比如系统调用、信号传递、进程状态变更等(strace底层使用内核的ptrace特性来实现其功能)。
strace有两种运行模式。
- 一种是通过它启动要跟踪的进程。用法很简单,在原本的命令前加上strace即可。比如我们要跟踪
ls -lh /var/log/messages这个命令的执行,可以这样:strace ls -lh /var/log/messages。 - 另外一种运行模式,是跟踪已经在运行的进程,在不中断进程执行的情况下进行分析。这种方式通过传递pid启动
strace -p [pid]。
strace常用选项:
-t跟踪的每一行都以时间为前缀。-tt在每行输出的前面,显示毫秒级别的时间-T显示每次系统调用所花费的时间,这将记录每个系统调用的开始和结束之间的时间差。-v对于某些相关调用,把完整的环境变量,文件stat结构等打出来。-f跟踪目标进程,以及目标进程创建的所有子进程-e expr控制要跟踪的事件和跟踪行为,比如指定要跟踪的系统调用名称-e trace=set仅跟踪指定的系统调用集。该选项用于确定哪些系统调用可能是跟踪有用有用。例如,trace=open,close,read,write表示仅跟踪这四个系统调用。-e trace=file跟踪所有以文件名作为参数的系统调用。-e trace=process跟踪涉及过程管理的所有系统调用。
-o把strace的输出单独写到指定的文件-s当系统调用的某个参数是字符串时,最多输出指定长度的内容,默认是32个字节-p指定要跟踪的进程pid, 要同时跟踪多个pid, 重复多次-p选项即可。-c统计系统调用的所执行的时间,次数和出错的次数等。-S按指定条件对-c选项打印的直方图输出进行排序。
1 | strace -c uptime |
perf
perf是Linux内核自带的一款性能分析工具,它利用内核中的性能计数器(performance counters)来收集系统的各种性能数据。这些数据包括CPU使用率、缓存命中率、分支预测失败率等,可以帮助我们深入了解系统的运行情况,找出性能瓶颈,进而进行优化。
perf的主要功能
- 数据收集:perf可以收集CPU、内存、IO等各个方面的性能数据,通过对这些数据的分析,我们可以找出系统中的性能瓶颈。
- 实时分析:perf提供了实时分析功能,可以在系统运行过程中实时查看性能数据,帮助开发人员及时发现问题并进行调整。
- 事件跟踪:perf支持事件跟踪功能,可以跟踪系统中的特定事件,如函数调用、系统调用等,从而找出性能问题的根源。
perf list可以列出所有可用的性能事件。这些事件包括硬件事件(如 CPU 周期、缓存命中/未命中等)和软件事件(如上下文切换、系统调用等)。
perf record
使用 perf record 命令来记录目标程序的性能数据。
1 | timeout 10s perf record -g -p 1156 |
-e选择性能事件。可以是 CPU 周期、缓存未命中等。使用 perf list 命令来查看所有可用的事件。-g记录函数调用关系(调用栈)。这对于分析程序中函数调用和热点非常有帮助。-a表示对所有 CPU 进行采样-p记录指定进程的性能数据。只关注某一个特定进程-t记录指定线程的性能数据。用于分析多线程程序中某个特定线程的性能-F 99设置采样频率,表示每秒采样 99 次-c 1000设置事件的采样周期。例如,每发生1000次事件采样一次。
这会生成一个 perf.data 文件,它包含了采集的性能数据,也可以用 -o 来指定生成的文件名。
可以指定要分析的事件类型,例如 CPU 时钟周期、缓存命中等。
1 | perf record -e cpu-clock -a sleep 6 |
分析性能数据
使用perf report产生结果分析。
1 | Samples: 717 of event 'cycles:uppp', Event count (approx.): 195060525 |
| 列 | 描述 |
|---|---|
| Children | 该函数及其所有子函数的 CPU 使用百分比总和 |
| Self | 该函数本身 CPU 使用百分比 |
| Command | 正在运行的程序或命令 |
| Shared Object | 包含该函数的库或模块 |
| Symbol | 函数或方法的名称 |
文件查看不够直观,还有一种火焰图分析的方式:
- 工具下载:
git clone https://github.com/brendangregg/FlameGraph.git - 使用命令:
- 使用perf script工具对perf.data进行解析
perf script -i perf.data &> perf.unfold - 将perf.unfold中的符号进行折叠:
/data/stackcollapse-perf.pl perf.unfold &> perf.folded - 最后生成svg图:
/data/flamegraph.pl perf.folded > perf.svg
- 使用perf script工具对perf.data进行解析
CPU 内部耗时
在CPU流水线简介中介绍了 CPU 内部的流水线执行模型。考虑到 CPU模型内部耗时,90%的CPU利用率可能意味着:

也可能意味着:

当CPU执行时,所需的数据不在寄存器或cache中。需要去内存加载数据,这期间CPU没有工作,等待,称stall。
当然还有可能是其他因素,为了了解了解 CPU 内部的耗时指标,我们可以通过 perf stat命令。下面是一个例子:
1 | # 也可以通过 -e 选项增加自己感兴趣的事件 |
这里的关键指标是每个周期的指令(insns每周期:IPC),它显示了平均每个CPU时钟周期我们完成了多少条指令。越高越好(一个简化)。上面0.78的例子听起来不错(78%忙?)但是考虑到这个处理器的最高速度是4.0的IPC。这也被称为4宽,这意味着,CPU可以在每个时钟周期停用(完成)四条指令。因此,在4宽系统上0.78的IPC意味着CPU以其最高速度的19.5%运行。较新的Intel处理器可能会转向5宽。
- task-clock:用于执行程序的CPU时间,单位是ms(毫秒)。第二列中的CPU utillized则是指这个进程在运行perf的这段时间内的CPU利用率,该数值是由task-clock除以最后一行的time elapsed(也就是wall time,真实时间,单位为秒,下面的M/sec等数值都是除以这个数得到的)再除以1000得出的。
- context-switches:程序在运行过程中发生的上下文切换次数。这个指标值大家都很熟悉,就不细说了。
- cpu-migrations:程序在运行过程中发生的CPU迁移次数,即被调度器从一个CPU转移到另外一个CPU上运行。这里要注意下CPU迁移和上下文切换的不同之处:发生上下文切换时不一定会发生CPU迁移,而发生CPU迁移时肯定会发生上下文切换。发生上下文切换时有可能只是把上下文从当前CPU中换出,下一次调度器还是将进程安排在这个CPU上执行。
- page-faults:缺页。指当内存访问时先根据进程虚拟地址空间中的虚拟地址通过MMU查找该内存页在物理内存的映射,没有找到该映射,则发生缺页,然后通过CPU中断调用处理函数,从物理内存中读取。
- cycles:CPU时钟周期。CPU从它的指令集(instruction set)中选择指令执行。一个指令包含以下的步骤,每个步骤的执行都至少需要花费一个时钟周期。
- 指令读取(instruction fetch)
- 指令解码(instruction decode)
- 执行(execute)
- 内存访问(memory access)
- 寄存器回写(register write-back)
- stalled-cycles:停滞周期,是指令管道未能按理想状态发挥并行作用,发生停滞的时钟周期。stalled-cycles-frontend指指令读取或解码的指令步骤,而stalled-cycles-backend则是指令执行步骤。
- instructions:该进程在这段时间内完成的CPU指令,这是整个perf stat命令输出中最重要的指标值。
- 第二列中的insns per cycle,简称IPC,表示一个时钟周期内能完成多少个CPU指令。该值越高,表示CPU的性能越好。
- 第二列第二行的stalled cycles per insn(上图由于不支持stalled-cycles所以未显示),表示完成每个指令,有多少个时钟周期是被停滞的,这个值越小,表示CPU的性能越好。该值是由stalled-cycles-frontend除以instructions得到的。
- branches:这段时间内发生分支预测的次数。
- branches-misses:这段时间内分支预测失败的次数,这个值越小越好。