中断亲和性概念:CPU亲和性、中断亲缘性与NUMA架构
好,咱们正式进入第二章。在动手配置之前,有些基础概念得先理清楚。说白了,中断亲和性就是解决一个问题——“谁该来处理这个中断?”
你想想看,一个网络包到了网卡,网卡发出中断信号。CPU A离得近,但正在忙;CPU B闲着,可它在另一个物理插槽上。选谁?选错了,性能就白白浪费了。
我刚开始接触这个领域时,也踩过坑。明明CPU占用率不高,但业务吞吐就是上不去。排查了半天,发现是中断被分到了一个跨NUMA的核上。从那以后,我养成了一个习惯:做性能调优,第一件事就是检查亲和性配置。
1. CPU亲和性:绑死你的进程
CPU亲和性,英文叫 CPU Affinity。它的核心思想很简单:把某个任务固定在某些CPU上执行。
为什么要这么干?因为CPU缓存是有温度的。一个进程在核0上跑,它的L1、L2缓存里全是它的数据。如果你放任它在核0到核3之间乱窜,每次切换都要重新加载缓存——这代价可不小。
关键数字:
- L1缓存命中:约1-2纳秒
- L1缓存未命中:约10-20纳秒
- 跨CPU缓存迁移:数十到上百纳秒
在Linux里,CPU亲和性通过sched_setaffinity()系统调用来设置。用taskset工具可以快速绑定。
# 把进程PID 1234绑定到CPU 0和CPU 2上
taskset -p 0x5 1234
上面的0x5是什么意思?二进制是0101,从右往左看:第0位是1(核0),第2位是1(核2)。说白了,这就是一个位掩码。
我个人的习惯:
对性能敏感的应用程序,优先绑在连续的物理核心上,不要跨CCX(AMD)或cluster(ARM)。跨了,延迟就不可控了。
2. 中断亲缘性:中断也分家
中断亲缘性,Interrupt Affinity。它和CPU亲和性很像,但对象不同——这次绑的是硬件中断。
每个硬件设备都有自己的中断号(IRQ)。你可以告诉操作系统:这个IRQ的中断,只准在哪些CPU上触发。
在x86系统上,配置入口在/proc/irq/{IRQ_NUM}/smp_affinity这个文件里。写进去的,是一个十六进制的位掩码。
# 查看网卡(假设IRQ 77)的中断亲缘性
cat /proc/irq/77/smp_affinity
# 输出:00000000,00000000,00000000,0000000f
# 说明:默认所有CPU都能处理
# 把它绑到CPU 0-3上
echo "00000000,00000000,00000000,0000000f" > /proc/irq/77/smp_affinity
避坑指南:
我曾经见过有人直接把smp_affinity改成0x1,以为只绑一个核性能最好。结果呢?高吞吐场景下,那个核的中断处理满载,应用反而被饿死了。嗯,中断处理也得留点余量,别把鸡蛋放一个篮子里。
更现代一点的做法,是用irqbalance这个守护进程。它会自动帮你把中断分散到各个CPU上。但在NUMA场景下,它的策略往往不够精细——这时候就得手动调了。
3. NUMA架构:内存不是等距的
NUMA,Non-Uniform Memory Access,翻译过来叫“非统一内存访问”。说白了就是:CPU访问不同位置的内存,速度不一样。
在NUMA系统里,物理内存被划分成多个“节点”(Node)。每个节点挂在一组CPU上。
- 本地访问:CPU访问自己所在节点的内存,延迟低,带宽高。
- 远程访问:CPU访问另一个节点的内存,要通过QPI/UPI/Infinity Fabric总线,延迟高,带宽低。
典型延迟对比(高端服务器):
| 访问类型 | 延迟(ns) | 带宽(GB/s) |
|---|---|---|
| 本地DRAM | ~80 | ~50 |
| 远程DRAM(跨1跳) | ~130 | ~35 |
| 远程DRAM(跨2跳) | ~180 | ~25 |
瞧见没?跨节点访问,延迟几乎翻倍。如果你的中断处理程序在Node 0上跑,但它处理的数据缓冲区在Node 1的内存里——每次访问内存都得多等几十纳秒。积少成多,性能就崩了。
所以,NUMA架构下的中断亲和性配置,核心原则就一条:中断在哪,内存就分配在哪。
4. 三者关系:一张图说清楚
你可能会问:这三者到底是什么关系?我用一张图来总结。
这张图表达了三个层次:
- NUMA节点划分:每个节点有自己的CPU核心和本地内存。
- CPU亲和性:中断处理程序被绑定到特定CPU核心上。
- 中断亲缘性:硬件中断请求被发送到特定NUMA节点内的CPU。
理想状态下,一个中断的处理流程应该是这样的:
- 硬件设备在Node 0的PCIe总线上
- 中断亲缘性配成Node 0的CPU
- 中断处理程序的内存分配在Node 0的本地内存上
- 应用程序也跑在Node 0上
这样一套组合拳下来,所有操作都在本地完成,没有跨节点开销。这是性能最优解。
我的经验法则:
在一台双路服务器上,如果你不确定怎么做,先把网卡中断全部绑在离网卡物理插槽最近的那个NUMA节点上。这通常就是最优方案。别问为什么,这是硬件设计决定的——最近的PCIe控制器就在那里。
本章小结
我们一口气讲了三个概念:
- CPU亲和性:把进程/线程绑死到特定CPU,避免缓存抖动。
- 中断亲缘性:控制硬件中断在哪些CPU上触发,避免中断处理跨NUMA。
- NUMA架构:内存访问延迟不对称,核心原则是“本地访问优先”。