4、CPU性能调优:从指标到实战

CPU调优这事儿,说难不难,说简单也不简单。我见过太多人一上来就盯着CPU使用率看,觉得使用率高了就是有问题。其实不然。今天咱们就把CPU调优的五个核心模块掰开揉碎,一个个讲清楚。

4.1 CPU性能指标解读

先说说怎么看CPU的“体检报告”。我个人习惯用top、vmstat、mpstat这三个工具打底。

指标 含义 正常范围
%user 用户态CPU时间占比 通常<70%
%sys 内核态CPU时间占比 通常<30%
%iowait 等待I/O的CPU时间占比 通常<5%
%steal 虚拟机偷取时间 通常<10%
load average 系统平均负载 不超过CPU核数

这里有个坑,我踩过。有一次线上告警,CPU使用率才30%,但系统响应慢得像蜗牛。查了半天,发现是%iowait飙到了40%。说白了,CPU在等磁盘干活,它闲着呢。所以别只看使用率,%iowait和load average要一起看。

核心原则:CPU使用率低 + 负载高 = 大概率在等I/O。CPU使用率高 + 负载高 = 计算密集型任务。

4.2 上下文切换优化

上下文切换,说白了就是CPU从一个任务切到另一个任务。每次切换都有开销,大概几微秒。频繁切换,性能就崩了。

怎么查?用vmstat 1看cs列(context switch)。如果每秒切换超过几万次,就得注意了。

# 查看上下文切换
vmstat 1 5
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  1234 56789    0    0    10    20  500 3000 15  5 80  0  0

你看,cs是3000,还算正常。如果超过10000,就要排查了。

优化的思路其实就三条:

  • 减少线程数:线程太多,切换自然多。我建议用线程池控制并发数。
  • 减少锁竞争:锁会导致线程阻塞,阻塞了就切换。用无锁数据结构或者读写锁优化。
  • 调整时间片:内核参数kernel.sched_min_granularity_ns可以调,但一般不动。

我的经验:有一次一个Java服务,线程数开到500,上下文切换每秒8万次。后来用线程池限制到50,切换降到5000,吞吐量翻了一倍。你想想看,这差距多大。

4.3 CPU亲和性绑定

CPU亲和性,就是把进程或线程绑定到特定CPU核上。好处是减少缓存失效和上下文切换。

为什么有效?因为CPU有L1/L2缓存,如果进程老在不同核上跑,缓存就白费了。绑定后,缓存命中率能提高不少。

# 使用taskset绑定进程到CPU 0-3
taskset -c 0-3 ./myapp

# 查看进程当前绑定
taskset -p 1234

我建议对关键业务进程做绑定。比如数据库、消息队列这些。但别绑太死,留几个核给系统用。

注意:别把所有核都绑上。系统进程也需要CPU,否则会出问题。我曾经把16核全绑给应用,结果SSH都连不上,尴尬了。

4.4 中断处理优化

中断,就是硬件通知CPU“我有事”。比如网卡收到数据包,会触发中断。中断处理不好,CPU就忙着响应中断,正经事干不了。

怎么查中断?用cat /proc/interrupts。你会看到每个CPU处理了多少中断。

# 查看中断分布
cat /proc/interrupts | grep eth0
           CPU0       CPU1       CPU2       CPU3
  0:        1000       2000       1500       1800   IR-IO-APIC   2-edge      eth0

如果中断都集中在CPU0上,那CPU0就累死了。解决办法是中断亲和性绑定,把中断分散到多个核。

# 设置中断亲和性(以eth0为例)
echo 0f > /proc/irq/24/smp_affinity

这里0f是位掩码,表示CPU 0-3。嗯,要注意,这个值要写十六进制。

避坑指南:我曾经在网卡中断没做均衡的机器上跑高并发服务,CPU0使用率100%,其他核闲着。做了中断亲和性后,CPU使用率均匀了,吞吐量提升了30%。

4.5 NUMA架构下的CPU调优

NUMA(非统一内存访问)架构下,CPU访问本地内存快,访问远端内存慢。差距可能达到2-3倍。

怎么查NUMA拓扑?用numactl --hardware。

# 查看NUMA节点
numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3
node 0 size: 16384 MB
node 1 cpus: 4 5 6 7
node 1 size: 16384 MB

你看,两个节点,每个节点4核16G内存。如果进程在node0上跑,但内存分配在node1上,性能就差了。

优化策略:

  • 绑定进程到同一NUMA节点:用numactl --cpunodebind=0 --membind=0 ./myapp
  • 使用interleave策略:如果内存访问均匀,可以用--interleave=all
  • 监控跨节点访问:用numastat看numa_hit和numa_miss

我的习惯:对于内存密集型应用,比如Redis、数据库,我一般会绑定到同一个NUMA节点。对于计算密集型,可以跨节点,但要注意内存分配策略。

知识体系总览

下面这张图,把CPU调优的五个模块串起来了。你可以把它当作一个检查清单。

CPU性能调优 性能指标解读 上下文切换优化 CPU亲和性绑定 中断处理优化 NUMA架构调优 常用工具:top / vmstat / mpstat / perf / numactl / taskset 图:CPU性能调优五大模块及工具链 箭头表示调优路径:指标 → 问题 → 优化手段

调优这事儿,没有银弹。每个场景都不一样。但只要你把指标看懂了,工具用熟了,再结合我上面说的这些经验,大部分问题都能搞定。

最后说一句:别一上来就调参数。先监控,再分析,最后动手。这是我从无数次翻车中总结出来的教训。

想要更多资料,联系可可