大家好,我是程栩,一个专注于性能的大厂程序员,分享包括但不限于计算机体系结构、性能优化、云原生的知识。
今天我们接着聊perf,聊一聊perf数据的来源,或者说perf事件。本文是perf系列的第三篇文章,后续会继续介绍perf,包括用法、原理和相关的经典文章。
引
前面我们说过,perf
是事件驱动的,而事件有多种类别,分别在计算机全栈的不同位置。我们今天将介绍这些事件,并进行一些简单的描述,后续将逐个进行原理的解说。
软件事件(Software events)
软件事件可以主要认为是内核的事件。打开perf list
我们可以看到一些预定义的内核事件,比如我们前文提到过的cpu-clock
、context-switches
等。如果想要知道具体的事件的含义,可以在perf_event_open
的文档中进行查看。perf_event_open
是perf
使用的系统调用,perf
将参数传入到里面再获取到相应的数据,这是perf
的工作机制,后续我们也会有相关的介绍。
在这里,我们给出一些事件相关的含义:
事件名 | 含义 |
---|---|
cpu-clock | cpu-clock,可以看成是cpu执行的计数器 |
task-clock | 某个任务在cpu上运行时的clock数 |
page-faults | 缺页中断的数量,如果过多的话会影响程序的运行速度 |
context-switches | 上下文切换的数量,如果切换的过多,可能想要执行的任务不一定能有足够的cpu时间 |
cpu-migrations | 任务在CPU间迁移的数量,过多的话可能会导致缓存失效 |
minor-faults | 是缺页中断的一部分,指的是访问在内存中但是没有映射到程序地址空间的页发生的错误 |
major-faults | 是缺页中断的一部分,指的是访问不在内存中的页,需要磁盘参与进行换页的中断 |
emulation-faults | 当内核遇到一些没有实现的指令时,可能会在用户空间模拟该执行,过多会影响运算效率 |
dummy | 一个类似占位符的事件,可以让用户收集数据而不需要一个计数事件 |
硬件事件(Hardware events)
硬件事件是perf
的出发点,让用户可以通过该机制获取到CPU上性能监控单元(PMU:performance monitoring unit )的数据,从而帮助用户了解硬件瓶颈在哪。这一部分的事件主要依赖于硬件厂商的支持,硬件厂商也在逐渐的给出更多更精确的硬件事件。值得注意的是,尽管硬件事件是在硬件实现,也不意味着这不会带来负载。假设我们用perf
进行数据的收集,perf
自身也是一个软件,处理硬件数据也会有一定的负载。同时,由于硬件事件需要通知软件来拿数据,会导致部分数据是「失真」的。比如在A时刻某事件计数器触发了,内核在A 2周期来拿数据,这会导致数据的失真。我们可以参考硬件厂商的手册来了解硬件事件,后续我们也会介绍类似PEBS这样的更高级的硬件事件特性。此外,在一般的虚拟机中,是不会有
同样的,我们也在这里给出一些硬件事件,以及他们相关的含义:
事件名 | 含义 |
---|---|
cache-misses | 缓存不命中的情况,过多的话会导致程序运行过慢 |
branch-misses | 分支预测失败的指令情况,过多的分支预测失败也会影响程序的运行速度 |
cpu-cycles | cpu的时钟情况,和cpu-clock不是一个概念 |
stalled_cycles_frontend | cpu前端的停滞周期数,cpu的前端是负责解码的部分,停滞可能是因为I-cache失效,如果停滞会导致后端空转 |
stalled_cycles_backend | cpu后端的停滞周期数,cpu的后端负责执行前端解出来的微指令,停滞可能是因为指令的关键路径较长或者访存拖慢了运行 |
内核追踪点(Kernel Tracepoints)
内核追踪点是被硬编码在内核中的钩子,当内核执行到某个地方的时候,就可能会触发到函数,从而将内核中的数据暴露出来。内核追踪点分布在内核的调度、内存管理、文件管理等模块。
由于是硬编码在内核中,优点的话就是其是静态的,不会影响内核本身的运行,相对应的负载也比较少;缺点也自然很明显,由于是硬编码的,那么每次更改就需要重新编译内核,相对而言更新的成本比较高。考虑到兼容性,内核追踪点的接口一般是不变化的。
我们可以用如下的方式获取到追踪点信息:
代码语言:javascript复制[root@VM-16-2-centos ~]# perf list | awk -F: '/Tracepoint event/ { lib[$1] } END {
> for (l in lib) { printf " %-16.16s %dn", l, lib[l] } }' | sort | column
内核追踪点主要包含以下几个模块:
- block:块设备I/O
- ext4:文件系统操作
- kmem:内核内存分配
- random:内核随机数生成器
- sched:CPU调度程序事件
- syscalls:系统调用进入和退出
一般来说,内核追踪点调用格式为模块名 事件。
USDT(User-Level Statically Defined Tracing)
USDT和内核追踪点类似,也是硬编码的,不过是硬编码在用户态的程序中。当我们面对一个黑箱一样的二进制时,如果其开启了USDT,我们就可以得到其给出的一些运行信息。USDT允许用户在应用程序一些特定的位置加入探针,从而帮助用户来获取数据。
其优点也比较明显,可以帮助我们不修改源码进行调试;但是这种调试是基于已有探针的情况,如何加入探针则又是一件令人头疼的事情,这是USDT的缺点。
Dynamic Tracing
动态追踪和追踪点类似,但并不完全一致,给出如下的两个图:
静态追踪点和动态追踪的区别
动态追踪允许我们查看系统上发生的任何事情,这意味着我们可以在不改动代码的情况下就获取到更多的信息。但是它的缺点也很明显,那就是会随着内核的变化而变化。
动态追踪借助了kprobe这样一个机制来实现,而USDT则借助uprobe来实现,具体如何我们会在后续继续介绍。
总结静态追踪点、USDT和动态追踪,我们会发现,变化越少的能力相对越弱,变化越大的能力则相对较强。
Timed Profiling
时序采样的数据来源于在具体频率下进行切片后的总和,也即perf record
。相比于前面的部分,更有时间的概念,可以帮助统计一段时间里的综合。
小结
今天我们更详细的介绍了perf
相关的数据来源,其在思维导图中的部分如下图所示:
小结
关注我,学习更多性能知识,一起攀登性能之巅。
参考资料
- perf_event_open(https://man7.org/linux/man-pages/man2/perf_event_open.2.html)
- perf Examples(https://www.brendangregg.com/perf.html#Events)