CPU性能篇:套路篇:CPU问题排查思路及优化思路

倪朋飞 《Linux 性能优化实战》
11 | 套路篇:如何迅速分析出系统CPU的瓶颈在哪里?
12 | 套路篇:CPU 性能优化的几个思路
13 | 答疑(一):无法模拟出 RES 中断的问题,怎么办?
14 | 答疑(二):如何用perf工具分析Java程序?

CPU 性能指标

复制代码
CPU 使用率;平均负载(Load Average);进程上下文切换;CPU 缓存的命中率
==================================================================================================
CPU 使用率
CPU 使用率描述了非空闲时间占总 CPU 时间的百分比,根据 CPU 上运行任务的不同,又被分为用户 CPU、系统 CPU、等待 I/O CPU、软中断和硬中断等。
    1.用户 CPU 使用率,包括用户态 CPU 使用率(user)和低优先级用户态 CPU 使用率(nice),表示 CPU 在用户态运行的时间百分比。用户 CPU 使用率高,通常说明有应用程序比较繁忙。
    2.系统 CPU 使用率,表示 CPU 在内核态运行的时间百分比(不包括中断)。系统 CPU 使用率高,说明内核比较繁忙。
    3.等待 I/O 的 CPU 使用率,通常也称为 iowait,表示等待 I/O 的时间百分比。iowait 高,通常说明系统与硬件设备的 I/O 交互时间比较长。
    4.软中断和硬中断的 CPU 使用率,分别表示内核调用软中断处理程序、硬中断处理程序的时间百分比。它们的使用率高,通常说明系统发生了大量的中断。
    5.在虚拟化环境中会用到的窃取 CPU 使用率(steal)和客户 CPU 使用率(guest),分别表示被其他虚拟机占用的 CPU 时间百分比,和运行客户虚拟机的 CPU 时间百分比。

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

---------------------------------------------------------------------------------------------------
进程上下文切换,包括:
    1.无法获取资源而导致的自愿上下文切换;
    2.被系统强制调度导致的非自愿上下文切换。
    上下文切换,本身是保证 Linux 正常运行的一项核心功能。
    但过多的上下文切换,会将原本运行进程的 CPU 时间,消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,缩短进程真正运行的时间,成为性能瓶颈。

---------------------------------------------------------------------------------------------------
CPU 缓存的命中率
    CPU 缓存的速度介于 CPU 和内存之间,缓存的是热点的内存数据。根据不断增长的热点数据,这些缓存按照大小不同分为 L1、L2、L3 等三级缓存,其中 L1 和 L2 常用在单核中, L3 则用在多核中。
    从 L1 到 L3,三级缓存的大小依次增大,相应的,性能依次降低(当然比内存还是好得多)。而它们的命中率,衡量的是 CPU 缓存的复用情况,命中率越高,则表示性能越好。
CPU 使用率;平均负载(Load Average);进程上下文切换;CPU 缓存的命中率
复制代码

 

 

复制代码
案例排查思路总结:平均负载的案例、上下文切换的案例、进程 CPU 使用率升高的案例、系统的 CPU 使用率升高的案例、不可中断进程和僵尸进程的案例、软中断的案例
=========================================================================================================
平均负载的案例
    我们先用 uptime, 查看了系统的平均负载;
    而在平均负载升高后,又用 mpstat 和 pidstat  ,分别观察了每个 CPU 和每个进程 CPU 的使用情况,进而找出了导致平均负载升高的进程,也就是我们的压测工具 stress。

    1.双核CPU环境中,平均负载接近1;mpstat/top发现单核的CPU使用率一直很高,另一核一直空闲;某进程的CPU使用率很高;
        说明某个高负荷进程一直跑在一颗CPU上;pidstat命令查看进程的用户CPU、系统CPU等数据
    2.双核CPU环境中,top发现平均负载接近1;单核idle较低(7%),sys 20%+,iowait 70+;
        说明某个高负荷进程一直跑在一颗CPU上;pidstat命令查看进程的用户CPU、系统CPU等数据,锁定进程
        上述2个案例平均负载在1左右是因为某个进程一直处于running状态导致的;平均负载是单位时间内的活跃进程数,但它实际上是活跃进程数的指数衰减平均值。
    3.双核CPU环境中,平均负载达到了7+,说明了多个任务在竞争CPU
        top命令的running任务数验证了这一点;通过top/pidstat的各个进程CPU数据,锁定了哪些进程正在竞争CPU

------------------------------------------------------------------------------------------------------------
上下文切换的案例。
    我们先用 vmstat  ,查看了系统的上下文切换次数和中断次数;
    然后通过 pidstat ,观察了进程的自愿上下文切换和非自愿上下文切换情况;
    最后通过 pidstat ,观察了线程的上下文切换情况,找出了上下文切换次数增多的根源,也就是我们的基准测试工具 sysbench。

    4.双核cpu环境中,top发现某个进程的cpu使用率达到194%,而平均负载却达到了7;系统CPU70%+,用户CPU20%+;running进程树一直也不多;所以猜测可能是多线程导致的CPU使用率高,平均负载高
        vmstat命令的r一直在7左右浮动;top的running却一直是1;(说明vmstat是统计到线程,而top统计的是进程);同时vmstat可以看到硬中断和上下文切换的数据维持在高位
        pidstat -wt验证了确实是多线程导致的高上下文切换
        cat  /proc/interrupts发现重调度中断(RES)变化最快,这个中断类型表示,唤醒空闲状态的 CPU 来调度新的任务运行。
        top、pidstat默认显示的是进程的指标;vmstat则显示线程的指标

------------------------------------------------------------------------------------------------------------
进程 CPU 使用率升高的案例。
    我们先用 top  ,查看了系统和进程的 CPU 使用情况,发现 CPU 使用率升高的进程是 php-fpm;
    再用 perf top  ,观察 php-fpm 的调用链,最终找出 CPU 升高的根源,也就是库函数 sqrt() 。
    
    5.单核CPU环境,在client使用ab命令对server端进行压测
        top命令显示running数据6,用户CPU99%+,而5个同名进程的CPU综合使用率正好99%+
        使用perf分析该进程,找到资源消耗高的函数,查看源码进行优化

------------------------------------------------------------------------------------------------------------
系统的 CPU 使用率升高的案例。
    我们先用  top 观察到了系统 CPU 升高,但通过 top 和 pidstat  ,却找不出高 CPU 使用率的进程。
    于是,我们重新审视 top 的输出,又从 CPU 使用率不高但处于 Running 状态的进程入手,找出了可疑之处,最终通过  perf record 和 perf report ,发现原来是短时进程在捣鬼。
    另外,对于短时进程,我还介绍了一个专门的工具 execsnoop,它可以实时监控进程调用的外部命令。

    5.双核CPU环境,在client使用ab命令对server端进行压测,
        top命令显示2颗CPU的用户CPU均60%+,系统CPU均23%+,idle很少;running的进程一直在8左右变化
        但是几个进程的CPU使用率相加,却远远达不到90%+的使用率;
        其实还是漏了running进程数量和CPU高的进程树对应不起来,根本原因在于短时进程造成的;通过排序,发现几个running进程,ps查看该进程却无该进程
        多次使用pstree命令搜索该短时应用,发现了一些蛛丝马迹。。。
        其实用execsnoop监视短时进程,效果很好。。

------------------------------------------------------------------------------------------------------------
不可中断进程和僵尸进程的案例。
    我们先用 top 观察到了 iowait 升高的问题,并发现了大量的不可中断进程和僵尸进程;
    接着我们用 dstat 发现是这是由磁盘读导致的,于是又通过 pidstat 找出了相关的进程。
    但我们用 strace 查看进程系统调用却失败了,最终还是用  perf 分析进程调用链,才发现根源在于磁盘直接 I/O 。

    6.双核CPU环境,top命令发现僵尸进程在不停增多;iowait持续维持在高位
        僵尸进程问题相对比较简单,使用pstree -aps $pid确认父进程,然后去应用内查看源码
        iowait维持在高位,使用dstat命令查看io数据,然后用pidstat -d锁定高io的应用;使用perf命令分析该应用

------------------------------------------------------------------------------------------------------------
软中断的案例。
    我们通过 top 观察到,系统的软中断 CPU 使用率升高;
    接着查看 /proc/softirqs, 找到了几种变化速率较快的软中断;
    然后通过 sar 命令,发现是网络小包的问题,
    最后再用  tcpdump  ,找出网络帧的类型和来源,确定是一个 SYN FLOOD 攻击导致的。

    7.单核CPU环境,top命令发现软中断很高,且cpu使用率最高的是ksoftirqd/0进程;
        /proc/softirqs查看软中断类型,发现是NET_RX(网络接收)
        使用sar -n DEV查看网络流量的历史发送情况
        最后用dumptcp命令锁定原因为sys攻击
案例排查思路总结:平均负载的案例、上下文切换的案例、进程 CPU 使用率升高的案例、系统的 CPU 使用率升高的案例、不可中断进程和僵尸进程的案例、软中断的案例
复制代码
复制代码
tips: pidstat中的%wait跟 top中的iowait%对比
===================================================================
tips:
 pidstat 中的 %wait 跟 top 中的 iowait% (缩写为 wa)对比,其实这是没有意义的,因为它们是完全不相关的两个指标。
    pidstat 中, %wait 表示进程等待 CPU 的时间百分比。
    top 中 ,iowait% 则表示等待 I/O 的 CPU 时间百分比。
    
    等待 CPU 的进程已经在 CPU 的就绪队列中,处于运行状态;R状态
    而等待 I/O 的进程则处于不可中断状态。D状态
    
tips: pidstat中的%wait跟 top中的iowait%对比
复制代码
复制代码
在执行 vmstat 时,第一行数据跟其他行相比较,数值相差特别大。

运行 man vmstat 命令,你可以在手册中发现下面这句话:
    The first report produced gives averages since the last reboot.  Additional reports give information on a sam‐ 
    pling period of length delay.  The process and memory reports are instantaneous in either case. 
也就是说,第一行数据是系统启动以来的平均值,其他行才是你在运行 vmstat 命令时,设置的间隔时间的平均值。另外,进程和内存的报告内容都是即时数值。
在执行 vmstat 时,第一行数据跟其他行相比较,数值相差特别大
复制代码

性能工具

第一个维度,从 CPU 的性能指标出发。也就是说,当你要查看某个性能指标时,要清楚知道哪些工具可以做到。

第二个维度,从工具出发。也就是当你已经安装了某个工具后,要知道这个工具能提供哪些指标。

复制代码
如何迅速分析 CPU 的性能瓶颈;几个例子
====================================================================
如何迅速分析 CPU 的性能瓶颈
    通常会先运行几个支持指标较多的工具,如 top、vmstat 和 pidstat ;这时为了缩小排查范围。
    通过这张图你可以发现,这三个命令,几乎包含了所有重要的 CPU 性能指标,比如:
        从 top 的输出可以得到各种 CPU 使用率以及僵尸进程和平均负载等信息。
        从 vmstat 的输出可以得到上下文切换次数、中断次数、运行状态和不可中断状态的进程数。
        从 pidstat 的输出可以得到进程的用户 CPU 使用率、系统 CPU 使用率、以及自愿上下文切换和非自愿上下文切换情况。

----------------------------------------------------------------------------
几个例子:
第一个例子,pidstat 输出的进程用户 CPU 使用率升高,会导致 top 输出的用户 CPU 使用率升高。
    所以,当发现 top 输出的用户 CPU 使用率有问题时,可以跟 pidstat 的输出做对比,观察是否是某个进程导致的问题。
    而找出导致性能问题的进程后,就要用进程分析工具来分析进程的行为,比如使用 strace 分析系统调用情况,以及使用 perf 分析调用链中各级函数的执行情况。

第二个例子,top 输出的平均负载升高,可以跟 vmstat 输出的运行状态和不可中断状态的进程数做对比,观察是哪种进程导致的负载升高。
    如果是不可中断进程数增多了,那么就需要做 I/O 的分析,也就是用 dstat 或 sar 等工具,进一步分析 I/O 的情况。
    如果是运行状态进程数增多了,那就需要回到 top 和 pidstat,找出这些处于运行状态的到底是什么进程,然后再用进程分析工具,做进一步分析。
    
最后一个例子,当发现 top 输出的软中断 CPU 使用率升高时,可以查看 /proc/softirqs 文件中各种类型软中断的变化情况,确定到底是哪种软中断出的问题。
    比如,发现是网络接收中断导致的问题,那就可以继续用网络分析工具 sar 和 tcpdump 来分析。
如何迅速分析 CPU 的性能瓶颈;几个例子
复制代码

 

 CPU优化

复制代码
优化问题:怎么评估性能优化的效果?多个性能问题同时存在,要怎么选择?有多种优化方法时,要如何选择?
===========================================================================================================
怎么评估性能优化的效果?
为了评估这个效果,我们需要对系统的性能指标进行量化,并且要分别测试出优化前、后的性能指标,用前后指标的变化来对比呈现效果。我把这个方法叫做性能评估“三步走”。
    1.确定性能的量化指标。
        性能的量化指标有很多,比如 CPU 使用率、应用程序的吞吐量、客户端请求的延迟等,都可以评估性能。
        建议是不要局限在单一维度的指标上,你至少要从应用程序和系统资源这两个维度,分别选择不同的指标。比如,以 Web 应用为例:
            a.应用程序的维度,我们可以用吞吐量和请求延迟来评估应用程序的性能。
            b.系统资源的维度,我们可以用 CPU 使用率来评估系统的 CPU 使用情况。
        之所以从这两个不同维度选择指标,主要是因为应用程序和系统资源这两者间相辅相成的关系。
            a.好的应用程序是性能优化的最终目的和结果,系统优化总是为应用程序服务的。所以,必须要使用应用程序的指标,来评估性能优化的整体效果。
            b.系统资源的使用情况是影响应用程序性能的根源。所以,需要用系统资源的指标,来观察和分析瓶颈的来源。
    2.测试优化前的性能指标。
    3.测试优化后的性能指标。
        这两个步骤,主要是为了对比优化前后的性能,更直观地呈现效果。

------------------------------------------------------------------------------------------------------------
多个性能问题同时存在,要怎么选择?
    “二八原则”,也就是说 80% 的问题都是由 20% 的代码导致的。只要找出这 20% 的位置,你就可以优化 80% 的性能。
        所以,并不是所有的性能问题都值得优化。
    建议动手优化之前先动脑,先把所有这些性能问题给分析一遍,找出最重要的、可以最大程度提升性能的问题,从它开始优化。
    这样的好处是,不仅性能提升的收益最大,而且很可能其他问题都不用优化,就已经满足了性能要求。

    推荐两个可以简化这个过程的方法。
        第一,如果发现是系统资源达到了瓶颈,
            比如 CPU 使用率达到了 100%,那么首先优化的一定是系统资源使用问题。完成系统资源瓶颈的优化后,我们才要考虑其他问题。
        第二,针对不同类型的指标,首先去优化那些由瓶颈导致的,性能指标变化幅度最大的问题。
            比如产生瓶颈后,用户 CPU 使用率升高了 10%,而系统 CPU 使用率却升高了 50%,这个时候就应该首先优化系统 CPU 的使用。

------------------------------------------------------------------------------------------------------------
有多种优化方法时,要如何选择?
    一般情况下,我们当然想选能最大提升性能的方法,这其实也是性能优化的目标。
    但要注意,现实情况要考虑的因素却没那么简单。最直观来说,性能优化并非没有成本。
    性能优化通常会带来复杂度的提升,降低程序的可维护性,还可能在优化一个指标时,引发其他指标的异常。也就是说,很可能你优化了一个指标,另一个指标的性能却变差了。

    所以,在考虑选哪个性能优化方法时,你要综合多方面的因素。
    切记,不要想着“一步登天”,试图一次性解决所有问题;也不要只会“拿来主义”,把其他应用的优化方法原封不动拿来用,却不经过任何思考和分析。
优化问题:怎么评估性能优化的效果?多个性能问题同时存在,要怎么选择?有多种优化方法时,要如何选择?
复制代码

 

复制代码
应用程序优化;系统优化;避免过早优化
======================================================================================================
应用程序优化
应用程序的性能优化也包括很多种方法,我在这里列出了最常见的几种
    1.编译器优化:很多编译器都会提供优化选项,适当开启它们,在编译阶段你就可以获得编译器的帮助,来提升性能。比如, gcc 就提供了优化选项 -O2,开启后会自动对应用程序的代码进行优化。
    2.算法优化:使用复杂度更低的算法,可以显著加快处理速度。比如,在数据比较大的情况下,可以用 O(nlogn) 的排序算法(如快排、归并排序等),代替 O(n^2) 的排序算法(如冒泡、插入排序等)。
    3.异步处理:使用异步处理,可以避免程序因为等待某个资源而一直阻塞,从而提升程序的并发处理能力。比如,把轮询替换为事件通知,就可以避免轮询耗费 CPU 的问题。
    4.多线程代替多进程:前面讲过,相对于进程的上下文切换,线程的上下文切换并不切换进程地址空间,因此可以降低上下文切换的成本。
    5.善用缓存:经常访问的数据或者计算过程中的步骤,可以放到内存中缓存起来,这样在下次用时就能直接从内存中获取,加快程序的处理速度。

------------------------------------------------------------------------------------------------------
系统优化
    从系统的角度来说,优化 CPU 的运行,
    一方面要充分利用 CPU 缓存的本地性,加速缓存访问;
    另一方面,就是要控制进程的 CPU 使用情况,减少进程间的相互影响。
    
    系统层面的 CPU 优化方法也有不少,这里我同样列举了最常见的一些方法,方便你记忆和使用。
        1.CPU 绑定:把进程绑定到一个或者多个 CPU 上,可以提高 CPU 缓存的命中率,减少跨 CPU 调度带来的上下文切换问题。
        2.CPU 独占:跟 CPU 绑定类似,进一步将 CPU 分组,并通过 CPU 亲和性机制为其分配进程。这样,这些 CPU 就由指定的进程独占,换句话说,不允许其他进程再来使用这些 CPU。
        3.优先级调整:使用 nice 调整进程的优先级,正值调低优先级,负值调高优先级。优先级的数值含义前面我们提到过,忘了的话及时复习一下。在这里,适当降低非核心应用的优先级,增高核心应用的优先级,可以确保核心应用得到优先处理。
        4.为进程设置资源限制:使用 Linux cgroups  来设置进程的 CPU 使用上限,可以防止由于某个应用自身的问题,而耗尽系统资源。
        5.NUMA(Non-Uniform Memory Access)优化:支持 NUMA 的处理器会被划分为多个 node,每个 node 都有自己的本地内存空间。NUMA 优化,其实就是让 CPU 尽可能只访问本地内存。
        6.中断负载均衡:无论是软中断还是硬中断,它们的中断处理程序都可能会耗费大量的 CPU。开启 irqbalance 服务或者配置 smp_affinity,就可以把中断处理过程自动负载均衡到多个 CPU 上。

------------------------------------------------------------------------------------------------------
避免过早优化
    很多人即使没发现性能瓶颈,也会忍不住把各种各样的优化方法带到实际的开发中。
    “过早优化是万恶之源”
    因为,一方面,优化会带来复杂性的提升,降低可维护性;
    另一方面,需求不是一成不变的。针对当前情况进行的优化,很可能并不适应快速变化的新需求。这样,在新需求出现时,这些复杂的优化,反而可能阻碍新功能的开发。
应用程序优化;系统优化;避免过早优化
复制代码

 

 

13 | 答疑(一):无法模拟出 RES 中断的问题,怎么办?
14 | 答疑(二):如何用perf工具分析Java程序?
这2章是针对前面课程的一些答疑,未总结!

posted @   雲淡風輕333  阅读(488)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示