CPU分析和优化总结

CPU 性能指标
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 时间百分比。
平均负载(Load Average), 系统的平均活跃进程
分别指过去 1 分钟、过去 5 分钟 和过去 15 分钟的平均负载。
理想情况下,平均负载等于逻辑 CPU 个数,这表示每个 CPU 都恰好被充分利用。如果平 均负载大于逻辑 CPU 个数,就表示负载比较重了。
进程上下文切换
- 无法获取资源而导致的自愿上下文切换;
- 被系统强制调度导致的非自愿上下文切换。
过多的上下文切换,会将原本运行进程的 CPU 时间,消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,缩 短进程真正运行的时间,成为性能瓶颈。
CPU 缓存的命中率
性能工具
案例
平均负载的案例
我们先用 uptime, 查看了系统的平均负载;而在平均负载升高后,又用 mpstat 和 pidstat ,分别观察了每个 CPU 和每个进程 CPU 的使用情况,进而 找出了导致平均负载升高的进程,也就是我们的压测工具 stress。
上下文切换的案例
先用 vmstat ,查看了系统的上下文切换次数和中断次 数;然后通过 pidstat ,观察了进程的自愿上下文切换和非自愿上下文切换情况;最后通过 pidstat ,观察了线程的上下文切换情况,找出了上下文切换次数增多的根源,也就是我们 的基准测试工具 sysbench。
进程 CPU 使用率升高的案例
我们先用 top ,查看了系统和进程的 CPU 使用情 况,发现 CPU 使用率升高的进程是 php-fpm;再用 perf top ,观察 php-fpm 的调用 链,最终找出 CPU 升高的根源,也就是库函数 sqrt() 。
系统的 CPU 使用率升高的案例
我们先用 top 观察到了系统 CPU 升高,但通过 top 和 pidstat ,却找不出高 CPU 使用率的进程。于是,我们重新审视 top 的输出,又从 CPU 使用率不高但处于 Running 状态的进程入手,找出了可疑之处,最终通过 perf record 和 perf report ,发现原来是短时进程在捣鬼。
不可中断进程和僵尸进程的案例
我们先用 top 观察到了 iowait 升高的问题,并 发现了大量的不可中断进程和僵尸进程;接着我们用 dstat 发现是这是由磁盘读导致的,于 是又通过 pidstat 找出了相关的进程。但我们用 strace 查看进程系统调用却失败了,最终 还是用 perf 分析进程调用链,才发现根源在于磁盘直接 I/O 。
软中断的案例。
我们通过 top 观察到,系统的软中断 CPU 使用率升高;接着查 看 /proc/softirqs, 找到了几种变化速率较快的软中断;然后通过 sar 命令,发现是网络 小包的问题,最后再用 tcpdump ,找出网络帧的类型和来源,确定是一个 SYN FLOOD 攻击导致的。
总结
第一个维度,从 CPU 的性能指标出发。

理解每个指标对应的工具的特性,一定更高效、 更灵活地使用。
第二个维度,从工具出发。

CPU的性能瓶颈分析
用户 CPU 使用率高,我们应该去排查进程的用户态而不是内核态。因为用户 CPU 使用率反映的就是用户态的 CPU 使用情况,而内核态的 CPU 使用情况只会反映到系统 CPU 使用率上。
为了缩小排查范围,先运行几个支持指标较多的工具,如 top、vmstat 和 pidstat 。 这三个命令,几乎包含了所有重要的 CPU 性能指标,比如:

第一个例子,pidstat 输出的进程用户 CPU 使用率升高,会导致 top 输出的用户 CPU 使 用率升高。所以,当发现 top 输出的用户 CPU 使用率有问题时,可以跟 pidstat 的输出做 对比,观察是否是某个进程导致的问题。
第二个例子,top 输出的平均负载升高,可以跟 vmstat 输出的运行状态和不可中断状态的 进程数做对比,观察是哪种进程导致的负载升高。如果是不可中断进程数增多了,那么就需要做 I/O 的分析,也就是用 dstat 或 sar 等工 具,进一步分析 I/O 的情况。如果是运行状态进程数增多了,那就需要回到 top 和 pidstat,找出这些处于运行状态的 到底是什么进程,然后再用进程分析工具,做进一步分析。
最后一个例子,当发现 top 输出的软中断 CPU 使用率升高时,可以查看 /proc/softirqs 文件中各种类型软中断的变化情况,确定到底是哪种软中断出的问题。比如,发现是网络接 收中断导致的问题,那就可以继续用网络分析工具 sar 和 tcpdump 来分析。
CPU 性能优化思路
-
首先,既然要做性能优化,那要怎么判断它是不是有效呢?特别是优化后,到底能提升多 少性能呢?
-
第二,性能问题通常不是独立的,如果有多个性能问题同时发生,你应该先优化哪一个 呢?
-
第三,提升性能的方法并不是唯一的,当有多种方法可以选择时,你会选用哪一种呢?是 不是总选那个最大程度提升性能的方法就行了呢?
-
第一个问题,直接 I/O 换成缓存 I/O,可以把 iowait 从 90% 降到接近 0,性能提升很明 显。
-
第二个问题,我们没有发现其他性能问题,直接 I/O 是唯一的性能瓶颈,所以不用挑选 优化对象。
-
第三个问题,缓存 I/O 是我们目前用到的最简单的优化方法,而且这样优化并不会影响 应用的功能。
但是,很多现实情况,性能评估可能有多重指标,性能问题可能会多个同时发生,而且,优化某一个指标的性能,可能又导致其他指标性能的下降。
怎么评估性能优化的效果
对系统的性能指标进行量化,并且要分别测试出优化前、后的性能指标,用前后指标的变化来对比呈现效果。
- 确定性能的量化指标。
- 测试优化前的性能指标。
- 测试优化后的性能指标。
第一步--选择指标来评估
不要局限在单一维度的指标上,至少要从应用程序和系统资源这两个维度,分别选择不同的指标。
比如,以 Web 应用为例:
应用程序的维度,我们可以用吞吐量和请求延迟来评估应用程序的性能。
系统资源的维度,我们可以用 CPU 使用率来评估系统的 CPU 使用情况。
应用程序和系统资源这两者间相辅相成的关 系。好的应用程序是性能优化的最终目的和结果,系统优化总是为应用程序服务的。所以,必须要使用应用程序的指标,来评估性能优化的整体效果。
系统资源的使用情况是影响应用程序性能的根源。所以,需要用系统资源的指标,来观察和分析瓶颈的来源。
第二三步
测试 Web 应用的并发请求数和响应延迟。而测试的同时,还可以用 vmstat、pidstat 等性能工 具,观察系统和进程的 CPU 使用率。这样,我们就同时获得了应用程序和系统资源这两个 维度的指标数值。
第一,要避免性能测试工具干扰应用程序的性能。通常,对 Web 应用来说,性能测试工具跟目标应用程序要在不同的机器上运行。
第二,避免外部环境的变化影响性能指标的评估。这要求优化前、后的应用程序,都运行在 相同配置的机器上,并且它们的外部依赖也要完全一致。
优化哪一个性能问题
80% 的问题都是由 20% 的代码导致的。只要找出这 20% 的位置,你就可以优化 80% 的性能。
并不是所有的性能问题都值得优化。
第一,如果发现是系统资源达到了瓶颈,比如 CPU 使用率达到了 100%,那么首先优化的 一定是系统资源使用问题。完成系统资源瓶颈的优化后,我们才要考虑其他问题。
第二,针对不同类型的指标,首先去优化那些由瓶颈导致的,性能指标变化幅度最大的问 题。
优化方法如何选择?
性能优化并非没有成本。性能优化通常会带来复杂度的提升,降低程序的可维护性,还可能在优化一个指标时,引发其 他指标的异常。也就是说,很可能你优化了一个指标,另一个指标的性能却变差了。
应用程序优化
- 排除所有不必要的工 作,只保留最核心的逻辑。比如减少循环的层次、减少递归、减少动态内存分配等等。
- 编译器优化:很多编译器都会提供优化选项
- 算法优化:使用复杂度更低的算法,可以显著加快处理速度。
- 异步处理:使用异步处理,可以避免程序因为等待某个资源而一直阻塞,从而提升程序的 并发处理能力。比如,把轮询替换为事件通知,就可以避免轮询耗费 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 上。
千万避免过早优化
优化会带来复杂性的提升,降低可维护性;
需求不是一成不变 的。针对当前情况进行的优化,很可能并不适应快速变化的新需求。这样,在新需求出现 时,这些复杂的优化,反而可能阻碍新功能的开发。
所以,性能优化最好是逐步完善,动态进行,不追求一步到位,而要首先保证能满足当前的 性能要求。当发现性能不满足要求或者出现性能瓶颈时,再根据性能评估的结果,选择最重 要的性能问题进行优化。
posted on 2025-10-12 21:40 chuchengzhi 阅读(96) 评论(0) 收藏 举报
浙公网安备 33010602011771号