CPU性能指标

性能指标总览

性能指标总览

CPU使用率

CPU 使用率描述了非空闲时间占总 CPU 时间的百分比,根据 CPU 上运行任务的不同,又被分为用户 CPU、系统 CPU、等待 I/0 CPU、软中断和硬中断等。用户 CPU 使用率,包括用户态 CPU 使用率(user)和低优先级用户态:

  • CPU 使用率(nice),表示 CPU 在用户态运行的时间百分比。用户 CPU 使用率高,通常说明有应用程序比较繁忙
  • 系统 CPU 使用率,表示 CPU 在内核状态运行的时间百分比(不包括中断)。系统 CPU 使用率高,说明内核比较繁忙
  • 等待 I/O 的 CPU 使用率,通常为 iowait,表示等待 I/O 的时间百分比。iowait 高,通常说明系统与硬件设备的 I/O 交互比较长
  • 软中断和硬中断的 CPU 利用率,分别表示内核调用软中断处理程序、硬中断处理程序的时间百分比。它们的使用率高,通常说明系统发生了大量的中断
  • 除了上述这些,还有在虚拟化环境中会用到的窃取 CPU 利用率(steal)和客户 CPU 使用率(guest),分别表示被其他虚拟机占用的 CPU 时间百分比,和运行客户虚拟机的 CPU 时间百分比

平均负载

系统的活跃进程数。反映了系统的整体负载情况,主要包括三个数值,分别表示过去 1 分钟、过去 5 分钟和过去 15 分钟的平均负载。理想情况下,平均负载等于逻辑 CPU 个数,这个代表每个 CPU 都恰好被充分利用。如果负载大于逻辑 CPU 个数,就表示负载比较重了。

进程上下文切换

进程上下文切换分为:自愿上下文切换和非自愿上下文切换

注意:过多的上下文切换,会将原本运行进程的 CPU 时间,消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,缩短进程真正进行的时间,成为性能瓶颈。

CPU 缓存命中率

CPU 缓存的速度介于 CPU 和内存之间,缓存的是热点的内存数据

如下图,根据不断增长的热点数据,这些缓存按照大小不同分为 L1、L2、L3 等三级缓存,其中 L1 和 L2 常在单核中,L3 则在多核中。从 L1 到 L3,三级缓存的大小依次增大,相应的,性能依次减少(当然内存还是好得多)。而它们的命中率,衡量的是 CPU 缓存的复用情况,命中率越高,则表示性能越好。

CPU 缓存

CPU 性能工具

平均负载案例:使用 uptime 查看平均负载,在平均负载升高时,使用 mpstat 和 pidstat 分别观察每个 CPU 和每个进程 CPU 的使用情况,找到导致平均负载升高的 stress 进程

上下文切换的案例:先使用 vmstat,查看系统上下文切换次数和中断次数;然后使用 pidstat(-w 参数)观察进程的自愿上下文切换和非自愿上下文切换;最后通过 vmstat(-wt参数)查看线程的上下文切换情况,从而找到了线程上下文切换增多的原因是 sysbench 工具

进程 CPU 使用率升高的案例:先使用 top 找出系统和进程 CPU 的使用情况,发现了 CPU 使用率很高的进程 php-fpm,再使用 perf top 找出热点函数 sqrt();如果是 Python 应用,可以使用 profiler 工具 pyflame 对指定进程分析(pyflame -p pid –threads -s 检测时间 -r 取样间隔 -o <file.txt>),再通过 flamegraph.pl 将输出的 txt 文件转换为 *.svg 格式的火焰图(./flamegraph.pl prof.txt > prof.svg)

不可中断进程和僵尸进程的案例:

  • 不可中断进程分析过程:先使用 top 查看,发现存在 D 状态(不可中断休眠进程)和 Z 状态(僵尸进程),并且 iowait 较高;使用 dstat 分析磁盘 I/O,发现 app 进程有大量的磁盘读请求;使用 pidstat(-d -p 参数)分析 app 进程的 I/O 操作,发现没有大量的 I/O 操作,再用 pidstat -d 分析系统的 I/O 情况,发现还是 app 进程在进行磁盘读;再使用 strace 跟踪 D 状态进程对应进程号的系统调用,发现没有权限;ps 查看发现对应进程号的进程已经变成僵尸进程;之后,通过 perf record -g 和 perf report 生成报告,查看 app 进程的调用栈,发现 CPU 使用主要是在 sys_read() 函数,定位到是在对磁盘进行直接读(direct_IO);查看代码发现 open() 系统调用使用了 O_DIRECT 参数
  • 僵尸进程分析:使用 pstree 命令找出僵尸进程的父进程是 app 进程,然后查看 app.c 文件,发现 wait() 使用位置不当导致不能回收子进程

软中断的案例:先使用 top 查看系统指标,发现系统 CPU 使用率很低,但是主要是在软中断 si 上,然后查看 /proc/softirqs 查看系统软中断变化情况,发现 NET_RX 变化率很快,再使用 sar 工具查看系统的网络收发情况,发现 eth0 网卡接收到了大量的小包;在通过抓包工具 tcpdump,发现 eth0 接受到了大量的 SYN 包,最终确定了是 SYN FLOOD 攻击

性能指标找工具

性能指标找工具

工具找指标

工具对应指标

如何分析 CPU 的性能瓶颈

重点:弄清楚性能指标之间的关联性

指标关联性

CPU 性能优化的几个思路

性能优化方法论

确定三个问题:

  • 判断所做的性能优化是否有效?优化后,能提升多少性能,有多少收益?
  • 如果有多个性能问题同时存在,应该先优先哪一个?
  • 当有多个优化方法,应该选择哪一种

怎么评估性能优化结果:

三步走原则:

  1. 确定优化的量化指标
  2. 测试优化前的性能指标
  3. 测试优化后的性能指标

第一步,性能的量化指标包括 CPU 使用率、应用的吞吐量,响应时间等等,不要局限在单一维度的指标上。例如,以 web 应用为例:

  • 应用程序的维度,使用吞吐量和请求延时来评估
  • 系统资源的维度,使用CPU 使用率来评估

好的应用程序是性能优化的最终结果和目的,要使用应用程序的指标,来评估性能优化的整体效果;而系统资源的使用情况是影响应用程序的根源,需要用资源的指标,来分析应用性能的瓶颈来源。

第二三步,对比第一部确定的量化指标在优化前后的差距,拿数据说话。例如,使用 ab 工具测试 Web 应用的并发请求数和响应延时,同时使用 vmstat,pidstat 等工具,观察系统和进程的 CPU 使用率,同时获得了应用和系统两个维度的性能指标

进行性能测试需要注意的是:

  • 要避免性能测试工具干扰应用程序的性能
  • 避免外部环境的变化影响性能指标的评估。在优化前、后的应用程序,都运行在相同配置的机器上,并且它们的外部依赖也要完全一致

多个性能问题同时存在,怎么选择?

遵循二八原则,80% 的性能问题都是由于 20% 的代码导致的,并不是所有的性能问题都值得优化

分析的步骤:

  • 挨个分析出所有的性能瓶颈,排除掉有因果关系的性能问题
  • 在剩下的几个性能问题中,选择能明显提升应用性能的问题进行修复,有两种方法:
    • 如果系统资源出现瓶颈,首先优化系统资源使用的问题
    • 针对不同类型的指标,首先优化导致性能指标变化幅度最大的那些瓶颈问题

有多种优化方法时,如何选择?

性能优化并非没有成本。

一个很典型的例子网络中的 DPDK(Data Plane Development Kit)。DPDK 是一种优化网络处理速度的方法,它通过绕开内核网络协议栈的方法,提升网络的处理能力。不过它有一个很典型的要求,就是要独占一个 CPU 以及一定数量的内存大页,并且总是以 100% 的 CPU 使用率运行。所以,如果你的 CPU 核数很少,就有点得不偿失了。

因此,在考虑性能优化方法时,要结合实际情况,考虑多方面的因素,进行权衡在做选择

CPU优化

常见的几种应用程序方法:

  • 编译器优化:很多编译器都会提供优化选项,适当开启它们,在编译阶段你就可以获得编译器的帮助,来提升性能。比如,gcc 就提供了优化选项 -O2,开启后会自动对应用程序的代码进行优化
  • 算法优化:使用复杂度更低的算法,显著加快处理速度
  • 异步处理:使用异步处理,可以避免程序因为等待某个资源而一直阻塞,从而提升程序的并发处理能力。比如,把轮询替换为事件通知,就可以避免轮询耗费 CPU 的问题
  • 多线程代替多进程:前面讲过,相对于进程的上下文切换,线程的上下文切换并不切换进程地址空间,因此可以降低上下文切换的成本
  • 善用缓存:经常访问的数据或者计算过程中的步骤,可以放到内存中缓存起来,这样在下次用时就能直接从内存中获取,加快程序的处理速度

常见的系统优化方法:

  • CPU 绑定:把进程绑定到一个或者多个 CPU 上,可以提高 CPU 缓存的命中率,减少跨 CPU 调度带来的上下文切换问题
  • CPU 独占:跟 CPU 绑定类似,进一步将 CPU 分组,并通过 CPU 亲和性机制为其分配进程。这样,这些 CPU 就由指定的进程独占,换句话说,不允许其他进程再来使用这些 CPU
  • 优先级调整:使用 nice 调整进程的优先级,正值调低优先级,负值调高优先级。可以适当降低非核心应用的优先级,增高核心应用的优先级,可以确保核心应用得到优先处理
  • 为进程设置资源限制:使用 Linux cgroups 来设置进程的 CPU 使用上限,可以防止由于某个应用自身的问题,而耗尽系统资源
  • NUMA(Non-Uniform Memory Access)优化:支持 NUMA 的处理器会将内存划分为多个 node,每个 node 关联到系统的一个处理器。NUMA 优化,其实就是让 CPU 尽可能只访问本地内存
  • 中断负载均衡:无论是软中断还是硬中断,它们的中断处理程序都可能会耗费大量的 CPU。开启 irqbalance 服务或者配置 smp_affinity,就可以把中断处理过程自动负载均衡到多个 CPU 上

避免过早优化:

性能优化最好是逐步完善,动态进行,不追求一步到位,而要首先保证能满足当前的性能要求。当发现性能不满足要求或者出现性能瓶颈时,再根据性能评估的结果,选择最重要的性能问题进行优化

总结

要忍住“把 CPU 性能优化到极致”的冲动,因为 CPU 并不是唯一的性能因素,还会有其他的性能问题,比如内存、网络、I/O 甚至是架构设计的问题。

如果不做全方位的分析和测试,只是单纯地把某个指标提升到极致,并不一定能带来整体的收益。