CPU性能分析:CPU使用率、负载均衡、上下文切换、中断处理

CPU性能分析,说白了就是搞清楚你的CPU到底在忙什么。我见过太多人一看到CPU飙到100%就慌了,其实不一定有问题。关键是要看懂背后的指标。

CPU使用率:别被数字骗了

CPU使用率是最直观的指标,但也是最容易被误解的。我习惯把它拆成三块来看:

  • 用户态使用率(user):应用程序在跑,这是好事
  • 系统态使用率(system):内核在干活,比如系统调用、中断处理
  • 等待I/O(iowait):CPU在等磁盘或网络,这是性能瓶颈的常见信号

举个例子,有一次我排查一个数据库慢查询问题,发现CPU使用率只有30%,但iowait高达40%。这说明CPU根本没闲着,而是在等磁盘把数据读回来。加了个SSD,问题就解决了。

核心判断原则

  • user高 + system低 = 应用在密集计算,正常
  • system高 + user低 = 可能有系统调用瓶颈或中断风暴
  • iowait高 = 磁盘或网络I/O是瓶颈

负载均衡:不是平均就完事了

负载均衡看的是CPU核之间的任务分配。你想想看,一个8核的机器,如果7个核在忙,1个核在闲着,那整体性能肯定上不去。

我常用的排查命令是mpstat -P ALL 1,每秒打印一次每个核的使用情况。如果发现某个核的使用率明显高于其他核,那就是负载不均衡。

# 查看每个CPU核的使用情况
mpstat -P ALL 1

# 输出示例
CPU    %usr   %sys   %iowait   %idle
0      15.2   3.1    0.5       81.2
1      85.3   8.2    1.0       5.5
2      12.1   2.5    0.3       85.1
3      14.8   3.0    0.4       81.8

看到没?CPU 1明显是热点。这种情况我遇到过好几次,多半是某个单线程应用或者中断亲和性设置有问题。

我的习惯:用taskset绑定进程到指定核,或者调整irqbalance服务,让中断均匀分布。

上下文切换:频繁切换是灾难

上下文切换,就是CPU从一个任务切到另一个任务。每次切换都有开销,保存寄存器、刷新TLB,这些都要时间。

怎么看上下文切换是否过多?用vmstat 1cs列(context switch)。如果每秒切换次数超过几万甚至十几万,那就要警惕了。

# 查看上下文切换频率
vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  0      0 123456  7890 456789    0    0    10    20  500 12000 15  5 80  0  0

这里的cs是12000,对于4核机器来说还算正常。但如果到了50000以上,那CPU大部分时间都在做切换,而不是真正干活。

我曾经踩过的坑:一个Java应用开了2000个线程,每个线程都在做很小的计算任务。结果上下文切换飙到每秒20万次,CPU全浪费在切换上了。后来用线程池限制到200个线程,性能直接翻倍。

中断处理:别让硬件打断你的节奏

中断是硬件通知CPU有事要处理的方式。比如网卡收到一个包,就会触发一个中断。中断太多,CPU就会被频繁打断,影响正常任务的执行。

cat /proc/interrupts可以查看每个中断发生的次数和分布到哪个CPU核。

# 查看中断分布
cat /proc/interrupts
           CPU0       CPU1       CPU2       CPU3
  0:        123        456        789        012   IO-APIC   2-edge      timer
  1:         10         20         30         40   IO-APIC   1-edge      i8042
 32:      10000      20000      30000      40000   PCI-MSI  eth0

看到eth0的中断了吗?它主要落在CPU3上,这就是我之前说的负载不均衡的一个原因。解决办法是设置中断亲和性,把中断分散到多个核上。

我建议:对于高并发的网络服务,开启RPS(Receive Packet Steering)或RFS(Receive Flow Steering),让内核自动把中断分散到多个CPU核上。

如何定位CPU瓶颈

定位CPU瓶颈,我有一套固定的排查流程:

  1. 看整体:用tophtop看CPU使用率、负载、上下文切换
  2. 看进程:用top -H看每个线程的CPU占用,找到最耗CPU的进程
  3. 看系统调用:用strace -c -p <pid>统计系统调用次数和耗时
  4. 看热点函数:用perf topperf record采样,找到CPU在哪个函数里花的时间最多
  5. 看中断:用cat /proc/interrupts检查中断分布是否均匀
# 使用perf定位热点函数
perf top -p <pid>

# 采样并生成报告
perf record -a -g -- sleep 10
perf report

嗯,这里要注意一点。perf的采样结果里,如果看到spin_lockmutex_lock这类函数占了很多时间,那说明有锁竞争问题。我遇到过好几次,都是多线程程序里锁粒度太大导致的。

总结一下我的经验:CPU瓶颈通常不是CPU不够快,而是CPU在等什么。等I/O、等锁、等内存,或者在做无意义的上下文切换。找到它在等什么,问题就解决了一半。

说白了,CPU性能分析就是一场「找茬游戏」。找到那个拖后腿的环节,优化它,性能自然就上去了。