性能优化-CPU篇

2,512 阅读16分钟

性能优化 是个系统工程,总是牵一发而动全身,它涉及了从程序设计、编程语言,再到系统、存储、网络等各种底层基础设施的方方面面。每一个组件都有可能出问题,而且很有可能多个组件同时出问题。

Linux 性能的基本指标、工具,以及相应的观测、分析和调优方法。包括 CPU 性能、磁盘 I/O 性能、内存性能以及网络性能。 系统性能问题,只要你理解了应用程序和系统的少数几个基本原理,再进行大量的实战练习,建立起整 体性能的全局观。

性能指标:“高并发”和“响应快”是从应用负载的视角来考察性能,直接影响了产品终端的用户体验。也正对应着性 能优化的两个核心指标——“吞吐”和“延时”。

随着应用负载的增加,系统资源的使用也会升高,甚至达到极限。而性能问题的本质 ,就是系统资源已经达到瓶颈,但请求的处理却还不够快,无法支撑更多的请求。性能分析,其实就是找出应用或系统的瓶颈,并设法去避免或者缓解它们,从而更高效地利用系统资源处理更多的请求。

性能优化方法论

  • 如何评估性能优化的效果?

确定性能的量化指标、测试优化前的性能指标、测试优化后的性能指标。量化指标的选择。至少要从应用程序和系统资源 这两个维度,分别选择不同的指标:

1)应用程序的维度,我们可以用吞吐量和请求延迟来评估应用程序的性能。 2)系统资源的维度,我们可以用 CPU 使用率来评估系统的 CPU 使用情况。 性能测试注意点:

1)避免性能测试工具干扰应用程序的性能; 2)避免外部环境的变化影响性能指标的评估。

  • 多个性能问题同时存在,选择优化哪些? 二八原则,并不是所有的性能问题都值得优化,找出最重要的、可以最大程度提升性能的问题。优先优化系统资源使用问题、性能指标变化幅度最大的问题。

  • 多种优化方法,选择哪种? 选能最大提升性能的方法,但性能优化通常会带来复杂度的提升,降低程序的可维护性,还可能引发其他指标的异常。

下面是我们进行系统优化的着手点。 image.png

本篇文章就从CPU着手介绍一下cpu相关的优化方向。

CPU优化

性能统计
  • 平均负载率 平均负载是指单位时间内,系统处于 可运行状态 和 不可中断状态 的平均进程数,也就是平均活跃进程数,它和 CPU 使用率并没有直接关系。

  • ①可运行状态 是指正在使用cpu或者等待cpu的进程,通过ps -aux 处于R(Running 或者 Runable)状态的进程

  • ②不可中断状态 是正处于内核态关键流程中的进程,并且这些流程是不可打断的, 比如最常见的是等待硬件设备的 I/O 响应,也就 是我们在 ps 命令中看到的 D 状(Uninterruptible Sleep,也称为 Disk Sleep)的进程

  • 查看平均负载率 命令 uptime 查看系统平均负载


[root@iZbp1agiqhn4s2q8a0980xZ ~]# uptime

15:00:00 up 535 days, 37 min, 1 user, load average: 0.04, 0.05, 0.05
1.15:00:25 // 当前时间 
2.up 535day  // 系统运行时间
3.user // 正在登录用户数 
4.load average: 0.00, 0.02, 0.05  // 依次则是过去 1 分钟、5 分钟、15 分钟的平均负载

.比如当平均负载为2时,意味着什么呢?

在只有2个CPU的系统上,意味着所有的 CPU 都刚好被完全占用。 在 4 个 CPU 的系统上,意味着 CPU 有 50% 的空闲。 而在只有 1 个 CPU 的系统中,则意味着有一半的进程竞争不到 CPU 另外还有一个最直接的显示系统平均负载的命令


cat /proc/loadavg

0.10 0.06 0.01 1/72 29632

除了前3个数字表示平均进程数量外,后面的1个分数,分母表示系统进程总数,分子表示正在运行的进程数;最后一个数字表示最近运行的进程ID.

  • 平均负载率设置多少合理   命令查看CPU 个数 grep 'model name' /proc/cpuinfo | wc -l 当平均负载高于CPU数量70%的时候,可以分析排查负载高的原因

  • 平均负载与CPU的关系 平均负载代表是活跃进程数,回到平均负载的含义,平均负载是单位时间内,处于可运行状态不可中断状态的进程数。所以它不仅仅包括正在运行的CPU进程,还包括等待CPU和等待IO的进程。 cpu使用率,定义是单位时间内cpu的繁忙情况的统计,跟平均负载不全对应:

  • cpu密集型进程,使用大量的cpu会导致平均负载过高,此时两者是一致的;

  • IO密集型进程,等待IO也会导致平均负载升高,但cpu不一定升高

  • 大量等待cpu的进程调度也会导致平均负载升高,此时cpu使用率会比较高

常用分析命令
  • mpstat

1.常用的多核 CPU 性能分析工具; 2.实时查看每个 CPU 的性能指标以及所有 CPU 的平均指标 运行 mpstat 查看 CPU 使用率的变化情况

# -P ALL 表示监控所有 CPU,后面数字 5 表示间隔 5 秒后输出一组数据
[root@centos7-2 ~]# mpstat -P ALL 5

image.png 排查是哪个进程导致 CPU 的使用率这么高的,可以使用 pidstat来查询


# pidstat[选项][<时间间隔>][<次数>]

# -u:默认的参数,显示各个进程的cpu使用统计

# -r:显示各个进程的内存使用统计

# -d:显示各个进程的IO使用情况

# -p:指定进程号

# -w:显示每个进程的上下文切换情况

# -t:显示选择任务的线程的统计信息外的额外信息

[root@centos7-2 ~]# pidstat -u 5 1

上线文切换
  • 概念理解

CPU 上下文切换,就是先把前一个任务的 CPU 上下文(CPU 寄存器和程序计数器)保存起来,然后加载 新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务,而保存下来的上下文, 会存储在系统内核中,并在任务重新调度执行时再次加载进来。

每次上下文切换都需要几十纳秒到到微秒的CPU时间,因此如果进程上下文切换次数过多,就会导致 CPU 将大量时间耗费在寄存器、内核栈以及虚拟内存等资源的保存和恢复上,进而大大缩短了真正运行进程的时间,实际上有效的CPU运行时间大大减少(可以认为上下文切换对用户来说是在做无用功)。

  • 上下文切换时机

  • 根据调度策略,将cpu时间划片对应的时间片。当时间片耗尽,就需要进行上下文切换;

  • 进程在系统资源不足,会在获取到足够的资源之前进程挂起

  • 进程通过sleep函数将自己挂起

  • 当优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会挂起,有高到低进程运行,也就是被抢占

  • 当发生硬中断,cpu上的进程会被中断挂起,转而执行内核中的中断服务程序

  • 上下文切分类

  • 进程上下文切换

  • 线程上下文切换

  • 中断上下文切换(硬件通过触发信号,会导致中断处理程序调用,也是一种常见的任务)

  • 查看上线文切换

  1. vmstat

是一个常用的系统性能分析工具,主要用来分析系统的内存使用情况,也常用来 分析 CPU 上下文切换和中断的次数

image.png

重点强调下,需要特别关注的四列内容:

  • cs(context switch)是每秒上下文切换的次数;

  • in(interrupt)则是每秒中断的次数。

  • r(Running or Runnable)是就绪队列的长度,也就是正在运行和等待 CPU 的进程数。

  • b(Blocked)则是处于不可中断睡眠状态的进程数。

vmstat 只给出了系统总体的上下文切换情况,要想查看每个进程的详细情况,就需要使用我们前面提到过的 pidstat 了。给它加上 -w 选项,就可以查看每个进程上下文切换的情况了。

  • us 用户进程执行消耗cpu时间(user time) us的值比较高时,说明用户进程消耗的cpu时间多,但是如果长期超过50%的使用,那么我们就该考虑优化程序算法或其他措施了

  • sy 系统进程消耗cpu时间(system time) sys的值过高时,说明系统内核消耗的cpu资源多,这个不是良性的表现,我们应该检查原因。这里us + sy的参考值为80%,如果us+sy 大于 80%说明可能存在CPU不足

  • id 空闲时间(包括IO等待时间) 一般来说 us+sy+id=100

  • wa 等待IO时间 wa过高时,说明io等待比较严重,这可能是由于磁盘大量随机访问造成的,也有可能是磁盘的带宽出现瓶颈。

  1. pidstat

[root@centos7-2 ~]# pidstat -w -u 3

image.png

结论: 结果中有两列内容是我们的重点关注对象。
一个是cswch,表示每秒自愿上下文切换(voluntary context switches)的次数,
另一个则是nvcswch,表示每秒非自愿上下文切换(non voluntary context switches)的次数。

  • 所谓自愿上下文切换,是指进程无法获取所需资源,导致的上下文切换。比如说,I/O、内存等系统资源不足时,就会发生自愿上下文切换。
  • 而非自愿上下文切换,则是指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换。比如 说,大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换。
CPU使用率
  • 概念

用什么指标来描述系统的 CPU 性能呢 ? 不是平均负载,也不是CPU上下文切换,而是另一个更直观的指标CPU使用率 ,CPU 使用率是单位时间内 CPU 使用情况的统计,以百分比的方式展示

  • 节拍率

为了维护 CPU 时间,Linux 通过事先定义的节拍率(内核中表示为 HZ),触发时间中断,并使用全局变量 Jiffies 记 录了开机以来的节拍数。每发生一次时间中断,Jiffies 的值就加1

  • 查看节拍率

[root@iZbp1agiqhn4s2q8a0980xZ ~]# grep 'CONFIG_HZ=' /boot/config-$(uname -r)

CONFIG_HZ=1000

  • 用户节拍率 正因为节拍率 HZ 是内核选项,所以用户空间程序并不能直接访问。为了方便用户空间程序,内核还提供了一个用 户空间节拍率 USER_HZ,它总是固定为 100,也就是 1/100 秒。这样,用户空间程序并不需要关心内核中 HZ 被设 置成了多少,因为它看到的总是固定值 USER_HZ
USER_HZ=100

为了方便用户控件程序,内核还提供了一个用户控件的节拍率,它总是固定为100,也就是1/100秒,这样,用户控 件程序并需要关系内核中HZ被设置成了多少

cpu使用率公式

CPU使用率,就是除了空闲时间外的其他时间占总CPU时间的百分比,用公式来表示就是 image.png 根据这个公式,我们就可以从 /proc/stat 中的数据,很容易地计算出 CPU 使用率。当然,也可以用每一个场景的 CPU 时间,除以总的 CPU 时间,计算出每个场景的 CPU 使用率。

  • 性能工具计算方式

事实上,为了计算机CPU使用率,性能能工具一般都会间隔一段时间(比如 3 秒)的两次值,做差后,再计算出这段

时间的平均CPU使用率

image.png

命令查看CPU使用率

查看 CPU 使用率 top 、ps 和pidstat是最常用的性能分析工具 :

  • top 显示了系统总体的 CPU 和内存使用情况,以及各个进程的资源使用情况。

  • ps 则只显示了每个进程的资源使用情况。

  • pidstat分析每个进程CPU使用情况

1.top图解

image.png 2.pidstat分析每个进程cpu使用情况
top并没有细分进程的用户态CPU和内核态CPU,那要怎么查看每个进程的详细情况?
例子:pidstat 1 5 没秒打一次 打五次

image.png 3.图解pidstat

image.png

cpu使用率过高分析

1.查询使用率过高的进程

通过 top、ps、pidstas等命令

2.定位占用cpu高的是代码中的哪个函数

perf和GDB

GDB(The GNU Project Debugger), 这个功能强大的程序调试利器 ,GDB 调试程序的过程会中断程序运行,这在 线上环境往往是不允许的;

perf 是 Linux 2.6.31 以后内置的性能分析工具。它以性能事件采样为基础,不仅可以分析系统的各种事件和内核性 能,还可以用来分析指定应用程序的性能问题 ,使用 perf 分析 CPU 性能问题

  • 案例

下面介绍两种用法

1.第一种常见用法是 perf top,类似于 top,它能够实时显示占用 CPU 时钟最多的函数或者指令,因此可以用来查找 热点函数,使用界面如下所示:


[root@centos7-2 ~]# perf top
Samples: 724 of event 'cpu-clock', Event count (approx.): 125711088
Overhead Shared Object Symbol

45.11% [kernel] [k] generic_exec_single
...

输出结果中,第一行包含三个数据,分别是采样数(Samples)、事件类型(event)和事件总数量(Event count)。比如这个例子中,perf 总共采集了1000个 CPU 时钟事件,而总事件数则为 271937500。 采样数需要我们 特别注意,如果采样数过少(比如只有十几个),那下面的排序和百分比就没什么实际参考价值了

第一列 Overhead ,是该符号的性能事件在所有采样中的比例,用百分比来表示。

第二列 Shared ,是该函数或指令所在的动态共享对象(Dynamic Shared Object),如内核、进程名、动态链接库

名、内核模块名等。

第三列 Object ,是动态共享对象的类型。比如 [.] 表示用户空间的可执行程序、或者动态链接库,而 [k] 则表示内核 空间。

最后一列 Symbol 是符号名,也就是函数名。当函数名未知时,用十六进制的地址来表示。

  • perf 命令详解 image.png
  1. 第二种常见用法,也就是 perf recordperf reportperf top 虽然实时展示了系统的性能信息,但它的缺点是并不 保存数据,也就无法用于离线或者后续的分析。而 perf record 则提供了保存数据的功能,保存后的数据,需要你用 perf report 解析展示

perf record # 按 Ctrl+C 终止采样

[root@centos7-2 ~]# perf report

Samples: 5K of event 'cpu-clock', Event count (approx.): 1332500000

Overhead Command Shared Object Symbol

97.15% swapper [kernel.kallsyms] [k] native_safe_halt

0.49% swapper [kernel.kallsyms] [k] _raw_spin_unlock_irqrestore

...

cpu性能调优策略

CPU 优化 我们接下来从应用程序系统的角度,分别来看看 如何才能降低 CPU 使用率,提高 CPU 的并行处理能

  • 应用程序优化
    1.首先,从应用程序的角度来说,降低 CPU 使用率的最好方法当然是,排除所有不必要的工作,只保留最核心的逻辑。比如减少循环的层次、减少递归、减少动态内存分配等等。
    这里列举了最常见的一些方法,方便记忆和使用。
    1) 编译器优化:很多编译器都会提供优化选项,适当开启它们
    2) 在编译阶段你就可以获得 编译器的帮助,来提升性能
    3) 算法优化 :使用复杂度更低的算法,可以显著加快处理速度
    4)异步处理:使用异步处理,可以避免程序因为等待某个资源而一直阻塞,从而提升程序 的并发处理能力。比如, 把轮询替换为事件通知,就可以避免轮询耗费 CPU 的问题。
    5)多线程代替多进程:前面讲过,相对于进程的上下文切换,线程的上下文切换并不切换 进程地址空间,因此可以 降低上下文切换的成本。
    6)善用缓存:经常访问的数据或者计算过程中的步骤,可以放到内存中缓存起来,这样在 下次用时就能直接从内存 中获取,加快程序的处理速度
  • 系统优化
    从系统的角度来说,优化CPU的运行,一方面要充分利用CPU 缓存的本地性,加速缓存访问;另一方面,就是要控制进程的CPU 使用情况减少进程间的相互影响。
    具体来说,系统层面的CPU 优化方法也有不少,这里同样列举了最常见的一些方法,方便你记忆和使用 。

1.CPU绑定:把进程绑定到一个或者多个 CPU 上,可以提高

2.CPU缓存的命中率,减少跨 CPU 调度带来的上下文切换 问题

3.CPU独占:跟CPU绑定类似,进一步将 CPU 分组,并通过 CPU 亲和性机制为其分配进程。这样,这些 CPU 就由 指定的进程独占,换句话说,不允许其他进程再来使用这些 CPU

  1. 优先级调整:使用 nice 调整进程的优先级,正值调低优先级,负值调高优先级。

5.为进程设置资源限制:使用 Linux cgroups 来设置进程的 CPU 使用上限,可以防止由 于某个应用自身的问题,而耗尽系统资源。

6.中断负载均衡:无论是软中断还是硬中断,它们的中断处理程序都可能会耗费大量的 CPU。开启 irqbalance 服务或 者配置 smp_affinity,就可以把中断处理过程自动负载均衡到多个CPU上。