《Linux 性能优化实战》第二周--CPU性能篇(下)

个人感悟

这两周主要围绕CPU性能优化相关基础知识进行学习。对于CPU性能问题的处理,首先我们要了解CPU相关性能指标。

CPU性能指标

平均负载,上下文切换,CPU使用率,CPU缓存命中率等.

平均负载是指系统处于可运行状态和不可中断状态的平均进程数(一般平均负载高于CPU数量70%的时候需要注意排查)。 平均负载升高可能是CPU密集进程导致,也可能是I/O进程过多超负载导致. 可以用mpstatpidstat辅助分析.

uptime #查看最近1/5/15分钟平均负载
watch -d uptime #实时查看变化情况
mpstat -P ALL 5 #查看CPU使用率,间隔5秒输出
pidstat -u 5 1  #查看进程CPU使用率/IOwait

上下文切换,主要分为进程上下文切换,线程上下文切换以及中断上下文切换(可以利用多线程替代多进程来提升性能)。可以通过vmstat查看系统总体上下文切换和中断次数. pidstat -w 查看每个进程上下文切换次数, -wt输出线程指标.

watch -d cat /proc/interrupts #查看中断变化具体来源
  • 系统调用涉及内核态和用户态的切换因此实际发生两次CPU上下文切换,不过不涉及虚拟内存等资源也不切换进程.
  • 进程的切换都在内核态,保存内核状态和CPU寄存器之前,先保存进程的虚拟内存,栈等
  • 同一进程内的线程切换只需要保存线程的私有数据,寄存器等.
  • 中断上下文切换并不涉及进程的用户态,比进程优先级高

CPU使用率过高时一般都是结合top/pidstat/perf来进行分析定位. 如果定位不到CPU使用率高的进程,可以跟踪top查看是否存在短时进程. 调用pstree分析父进程.

perf record -g
perf report #查看性能报告找到瓶颈

execsnoop #监控短时进程

iowait升高时,可以用dstat观察CPU和I/O的使用情况.

pidstat -d -p XXX n m  #-d输出I/O情况,指定线程号间隔n秒输出m组数据
strace -p XXX #跟踪系统调用
如果定位进程状态为Z,通过perf report查看调用栈

僵尸进程, 通过pstree查看父进程,在父进程中进行解决(子进程创建和清理的地方)

pstree -aps XXX

中断处理

/proc/softirqs #查看软中断
/proc/interrupts #查看硬中断

网络监测工具

sar -n DEV 1 #网络收发报告, 间隔1秒输出一组数据
tcpdump -i eth0 -n tcp port 80 #指定网卡eth0,tcp协议,端口号80

CPU性能分析套路

通过几组实验分析我们发现对于CPU性能,常用的瓶颈问题有一些套路可以定位问题所在。虽然CPU性能指标很多,但是都不是孤立存在的,很多指标间都存在一定的关联。 再遇到CPU性能问题时我们可以先运行几个支持指标较多的工具(top/vmstat/pidstat)来缩小排查范围,查看是否是因为某个进程导致,找出导致性能问题的进程之后,再用进程分析工具来分析进程的行为。比如strace分析系统调用情况,或者perf分析调用链中各级函数的执行情况。

CPU性能优化方案

定位到问题所在之后并不是要立即着手进行优化。在需要优化之前,要先考虑优化到底能提升多少性能(应用程序和系统资源多维度评估),选取哪些性能问题进行优化以及优化方案的取舍. 优化往往会带来复杂度的提升,所以要做好性能和复杂度的平衡.

CPU优化的一些常用方法

  • 应用程序优化
    • 编译器优化
    • 算法优化
    • 异步处理
    • 多线程代替多进程
    • 善用缓存
  • 系统优化
    • CPU绑定
    • CPU独占
    • 优先级调整
    • 为进程设置资源限制
    • NUMA优化
    • 中断负载均衡

接下来是本周读书笔记


Lesson 9 怎么理解Linux软中断

本周继续学习CPU性能相关知识点,Linux 性能优化实战第一周第8课提到不可中断状态一般都是短时进程,主要是系统的一种保护机制,保证硬件的交互过程不被意外打断. 但是如果进程长时间处于不可中断状态就需要注意是否存在磁盘I/O问题.

除了iowait,软中断softirq导致CPU使用率增加的场景也比较常见.

为了解决中断处理程序执行时间过长和中断丢失的问题,Linux将中断过程分为两阶段:

  • 快速处理中断 (硬中断, 会打断CPU正在执行的任务)
  • 延迟处理上半部未完成的工作, 通常以内核线程的方式运行 (软中断,内核线程执行)

以网卡接收数据包为例: 网卡接收到数据包之后,先通过硬中断通知内核新数据到达.此时内核调用中断处理程序来响应. 第一步快速处理中断,将网卡数据读到内存中,然后更新硬件寄存器的状态(表示数据已经读完); 第二步发送软中断信号,内核线程从内存中找到网络数据,按照网络协议栈对数据逐层解析和处理,直到将其发送给应用程序.

软中断不仅包括上述硬件设备中断处理程序的第二阶段,还包含一些内核自定义的事件. 如内核调度,RCU锁等.

~ cat /proc/softirqs   #查看软中断的运行情况 10种不同软中断类型
                CPU0       CPU1       CPU2       CPU3       
      HI:     940848    8695970     929219     958387
   TIMER:    6032528    8523080    6185804    7882723
  NET_TX:         13         18         10      21655
  NET_RX:      81303      81483      74123    5497701
   BLOCK:        112        109    1961852         73
IRQ_POLL:          0          0          0          0
 TASKLET:         40        152      69233      41556
   SCHED:    5262219    6279886    5054733    5849054
 HRTIMER:          0          0          0          0
     RCU:    4615615    5897520    4693046    5749548

➜  ~ ps -aux | grep softirq #一个CPU对应一个软中断内核线程
  root         7  0.0  0.0      0     0 ?        S    Jun01   0:00 [ksoftirqd/0]
  root        16  0.0  0.0      0     0 ?        S    Jun01   0:01 [ksoftirqd/1]
  root        22  0.0  0.0      0     0 ?        S    Jun01   0:01 [ksoftirqd/2]
  root        28  0.0  0.0      0     0 ?        S    Jun01   0:02 [ksoftirqd/3]

Lesson 10 系统的软中断CPU使用率高,怎么解决

所需工具

工具 用途
sar 系统报告工具,实时查看当前系统活动,配置保存和报告历史统计
hping3 构造TCP/IP包, 对系统进行安全审计,防火墙测试等
tcpdump 网络抓包工具, 用来分析各种网络问题

实验与分析

sudo docker run -itd --name=nginx -p 81:80 nginx  #启动nginx服务
curl http://localhost:81  #检查nginx服务是否正常运行

hping3 -S -p 81 -i u100 localhost    #每100微秒发送一个网络帧

本机测试中并没有监测到系统响应变慢,改为1微秒时数据包全丢, 10微秒时丢包率为80%.

top查看ksoftirqd软中断进程CPU使用率也不高.

继续执行后续指令

watch -d cat /proc/softirqs #观察高亮变化

此时发现TIMER/NET_RX/SCHED/RCU都在不停变化,且NET_RX变化速率较快,几K级别的在增加. 其它几种类型的软中断是保证linux调度,时钟,临界区保护等正常工作必须,有变化是正常的.

因此我们着手分析网络接收的软中断,选取sar工具查看网络收发情况

sar -n DEV 1  #每隔1秒输出网络收发报告

07:01:11 PM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s rxcmp/s   txcmp/s  rxmcst/s   %ifutil
07:01:12 PM veth3a2b4de      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
07:01:12 PM        lo  20895.00  20895.00    843.42    843.42      0.00      0.00      0.00      0.00
07:01:12 PM   docker0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
07:01:12 PM enp0s31f6      6.00      0.00      0.85      0.00      0.00      0.00      0.00      0.00
07:01:12 PM vethedb691a      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
07:01:12 PM br-1665f3682889      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
  • rxpck/s txpck/s 表示每秒接收/发送的网络帧数  PPS
  • rxkB/s txkB/s  表示每秒接收/发送的千字节数 BPS 

看到网卡lo每秒接收的数据明显较多, 843*1024/20895 =41 说明每个网络帧只有41字节,小包问题.

进一步通过tcpdump分析这是什么样的网络帧

tcpdump -i lo -n tcp port 81

18:20:43.273056 IP 127.0.0.1.12537 > 127.0.0.1.81: Flags [R], seq 393134060, win 0, length 0
18:20:43.273184 IP 127.0.0.1.12538 > 127.0.0.1.81: Flags [S], seq 1240632262, win 512, length 0
18:20:43.273193 IP 127.0.0.1.81 > 127.0.0.1.12538: Flags [S.], seq 3698133257, ack 1240632263, win 65495, options [mss 65495], length 0
18:20:43.273198 IP 127.0.0.1.12538 > 127.0.0.1.81: Flags [R], seq 1240632263, win 0, length 0

可以看出网络帧是发送到nginx端口,Flags [S]表示这是一个SYN包, 可以锁定是SYN FLOOD问题,最简单的解决方法就是防火墙中封锁该来源IP.

总结

如果碰到软中断线程CPU使用率高的情况下, 可以借助sar和tcpdump等工具进一步分析来源.


Lesson 11 [套路] 如何迅速分析出系统CPU的瓶颈在哪里?

要想分析处理CPU性能问题, 首先我们需要了解CPU性能指标.

CPU性能指标

  • CPU使用率
    • 用户CPU使用率, 包括用户态(user)和低优先级用户态(nice). 该指标过高说明应用程序比较繁忙.
    • 系统CPU使用率, CPU在内核态运行的时间百分比(不含中断). 该指标高说明内核比较繁忙.
    • 等待I/O的CPU使用率, iowait, 该指标高说明系统与硬件设备I/O交互时间比较长.
    • 软/硬中断CPU使用率, 该指标高说明系统中发生大量中断.
    • steal CPU / guest CPU, 表示虚拟机占用的CPU百分比.
  • 平均负载

    理想情况下平均负载等于逻辑CPU个数,表示每个CPU都被充分利用. 若大于则说明系统负载较重.

  • 进程上下文切换

    包括无法获取资源的自愿切换和系统强制调度时的非自愿切换. 上下文切换本身是保证Linux正常运行的一项核心功能. 过多的切换则会将原本运行进程的CPU时间消耗在寄存器,内核占及虚拟内存等数据保存和恢复上.

  • CPU缓存命中率

    CPU缓存的复用情况,命中率越高性能越好. 其中L1/L2常用在单核,L3则用在多核中.

性能工具

回顾之前的几个CPU性能测试场景:

  • 平均负载案例
    • 先用uptime查看系统平均负载
    • 判断负载在升高后再用mpstat和pidstat分别查看每个CPU和每个进程CPU使用情况.找出导致平均负载较高的进程.
  • 上下文切换案例
    • 先用vmstat查看系统上下文切换和中断次数
    • 再用pidstat观察进程的自愿和非自愿上下文切换情况
    • 最后通过pidstat观察线程的上下文切换情况
  • 进程CPU使用率高案例
    • 先用top查看系统和进程的CPU使用情况,定位到进程
    • 再用perf top观察进程调用链,定位到具体函数
  • 系统CPU使用率高案例
    • 先用top查看系统和进程的CPU使用情况,top/pidstat都无法找到CPU使用率高的进程
    • 重新审视top输出
    • 从CPU使用率不高,但是处于Running状态的进程入手
    • perf record/report发现短时进程导致 (execsnoop工具)
  • 不可中断和僵尸进程案例
    • 先用top观察iowait升高,发现大量不可中断和僵尸进程
    • strace无法跟踪进程系统调用
    • perf分析调用链发现根源来自磁盘直接I/O
  • 软中断案例
    • top观察系统软中断CPU使用率高
    • 查看/proc/softirqs找到变化速率较快的几种软中断
    • sar命令发现是网络小包问题
    • tcpdump找出网络帧的类型和来源, 确定SYN FLOOD攻击导致

根据不同的性能指标来找合适的工具:

在生产环境中往往开发者没有权限安装新的工具包,只能最大化利用好系统中已经安装好的工具. 因此要了解一些主流工具能够提供哪些指标分析.

作者给了一个直观的套路, 先运行几个支持指标较多的工具, 如top/vmstat/pidstat,根据它们的输出可以得出是哪种类型的性能问题. 定位到进程后再用strace/perf分析调用情况进一步分析. 如果是软中断导致用/proc/softirqs


Lesson 12 CPU性能优化的几个思考

性能优化方法论

遇到性能问题,优化前首先思考三个问题:

  • 首先判断优化是否有效,能提升多少性能?
  • 多种性能问题同时存在情况下,先优化哪一个?
  • 提升性能的方法往往不唯一,如何选取? 是否总选最大程度提升的那一种?

怎样评估性能优化效果

  • 确定性能的量化指标
    • 不要局限在单一维度,至少从应用程序和系统资源两个维度,分别选择不同的指标
      • 应用程序: 吞吐量和请求延迟
      • 系统资源: CPU使用率
    • 好的应用程序是性能优化的最终目的和结果,系统资源的使用情况是影响应用程序性能的根源.
    • 例如, web程序可以用ab等工具测试并发请求数和响应延迟,同时可以用vmstat/pidstat观察系统和进程的CPU使用率
  • 测试优化前的性能指标
  • 测试优化后的性能指标
    • 要避免性能测试工具的干扰
    • 避免外部环境的变化

多个性能问题同时存在,如何选择?

并不是所有性能问题都值得优化,性能测试也存在二八法则

  • 如果是系统资源达到的瓶颈,首先优化系统资源的使用问题.
  • 针对不同类型的指标,首先优化那些由瓶颈导致,性能指标变化幅度较大的问题.

多个优化方法, 如何选择?

在提升性能的同时也要考虑优化成本.性能优化通常会带来复杂度的提升,降低程序的可维护性. (balance)

例如DPDK(Data Plane Development Kit)是一种优化网络处理速度的方法,通过绕开内核网络协议栈的方法,提升网络的处理能力. 但是它要求独占一个CPU以及一定数量的内存大页,并且以100%CPU使用率运行. 在CPU核数较少的情况下不适合.

CPU 优化

  • 应用程序优化
    • 编译器优化: 编译阶段开启优化选项, 如gcc -O2
    • 算法优化
    • 异步处理: 避免程序因为等待某个资源而一直阻塞,提升程序的并发处理能力. (将轮询替换为事件通知)
    • 多线程代替多进程: 减少上下文切换成本
    • 善用缓存: 加快程序处理速度
  • 系统优化
    • CPU绑定: 将进程绑定要1个/多个CPU上,提高CPU缓存命中率,减少CPU调度带来的上下文切换
    • CPU独占: CPU亲和性机制来分配进程
    • 优先级调整:使用nice适当降低非核心应用的优先级
    • 为进程设置资源显示: cgroups设置使用上限,防止由某个应用自身问题耗尽系统资源
    • NUMA优化: CPU尽可能访问本地内存
    • 中断负载均衡: irpbalance,将中断处理过程自动负载均衡到各个CPU上

避免过早优化

优化可能会带来复杂性的提升,降低可维护性.针对当前情况进行的优化可能不适应快递迭代的新需求.

因此性能优化最好逐步完善,根据性能评估的结果选择最重要的性能问题进行优化.

Lesson 13/14 答疑 (略)

读书群内分享

  • TPS QPS 系统吞吐量的区别和理解

    • QPS (Queries Per Second)每秒查询率,一台服务器每秒能够响应的查询次数.
    • TPS (Transactions Per Second)每秒事务数,软件测试的结果.

      • 用户请求服务器
      • 服务器内部处理
      • 服务器返回给客户

        QPS类似TPS,但是对于一个页面的访问形成一个TPS,但是一次页面请求可能包含多次对服务器的请求,可能计入多次QPS

    • 系统吞吐量, 包括几个重要参数:

      • QPS(TPS)
      • 并发数
      • 响应时间

        QPS(TPS)=并发数/平均相应时间

  • 深入理解Linux系统下proc文件系统内容