4、CPU亲和性与隔离:绑核、中断隔离、关闭超线程与节能状态
各位同学,今天我们来聊一个非常“硬核”的话题——CPU亲和性与隔离。
说实话,很多做量化交易的朋友,代码写得挺漂亮,算法也牛,但一到实盘就发现延迟不稳定。为什么?因为操作系统压根没把你的交易线程当“亲儿子”看待。它会把你的线程从这个核踢到那个核,缓存全丢,中断一来还得停下手里活去伺候硬件。
我个人习惯,在搭建交易系统时,第一件事就是先把CPU的“地盘”划清楚。谁该干什么,谁不能碰谁,都得定死。今天我就把这几招全教给你。
4.1 绑核:把线程钉死在CPU上
绑核,说白了就是告诉操作系统:“这个线程只能在这个核上跑,别给我乱动。”
为什么要绑?你想想看,线程在核之间迁移,代价有多大?L1/L2缓存全失效,TLB也得重新加载。一次迁移,几百纳秒就没了。在纳秒级交易里,这简直是灾难。
在Linux上,我一般用 pthread_setaffinity_np() 来绑核。代码很简单,但效果立竿见影。
// 绑定线程到CPU 2
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(2, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
嗯,这里要注意:绑核之前,先搞清楚你的CPU拓扑。别把两个超线程绑一起了,那等于白绑。我建议用 lscpu -e 先看一眼物理核和逻辑核的对应关系。
4.2 中断隔离:别让硬件打断你的交易
绑核只是第一步。你想想,就算线程钉死在一个核上,网卡中断来了,它还是得去处理。这一处理,几十微秒就没了。
我曾经在一个项目中遇到过,明明绑了核,延迟还是时不时飙到几十微秒。查了半天,发现是网卡中断和交易线程抢同一个核。后来用了中断隔离,问题立刻解决。
中断隔离,就是把某些CPU核从中断处理中“摘”出来。Linux内核提供了 irqbalance 和 /proc/irq/ 接口来控制。
# 查看中断亲和性
cat /proc/irq/78/smp_affinity
# 将中断绑定到CPU 0-1(十六进制0x03)
echo 03 > /proc/irq/78/smp_affinity
更彻底的做法,是在内核启动参数里直接隔离CPU核:
isolcpus=2,3 nohz_full=2,3 rcu_nocbs=2,3
这样,CPU 2和3就不会收到任何中断请求。交易线程可以安心跑,没人打扰。
4.3 关闭超线程:别被“假核”骗了
超线程(Hyper-Threading)是什么?就是一个物理核模拟成两个逻辑核。听起来不错,但对低延迟交易来说,这玩意儿是毒药。
为什么?因为两个逻辑核共享L1缓存和执行单元。一个核跑交易,另一个核跑别的任务,缓存一冲突,交易线程就得等。延迟直接翻倍。
我建议,在BIOS里直接关掉超线程。如果BIOS不方便,也可以在Linux启动时加参数:
maxcpus=4 nosmt
这样系统只识别物理核,不认逻辑核。干净利落。
| 配置 | 平均延迟 | 99.9%延迟 | 抖动 |
|---|---|---|---|
| 开启超线程 | 1.2μs | 8.5μs | 高 |
| 关闭超线程 | 0.9μs | 1.8μs | 低 |
你看,关闭超线程后,平均延迟降了25%,但最差情况延迟降了将近5倍。这就是稳定性的价值。
4.4 关闭节能状态:别让CPU“偷懒”
现代CPU都有节能功能,比如C-states(C1、C2、C3...)和P-states。这些状态会让CPU在空闲时降频、关闭部分电路,省电。
但问题是,交易系统不是一直忙的。行情来了才忙,行情间隙就空闲。CPU一空闲,立刻进入节能状态。等下一笔行情来了,CPU要从深度睡眠中“醒”过来,这一醒,几百纳秒到几微秒就没了。
我遇到过最夸张的一次,延迟从1微秒直接跳到50微秒。查了半天,发现是CPU进了C6状态,唤醒花了40多微秒。
解决方案?在BIOS里关掉所有C-states,只保留C0(运行状态)。或者用Linux的 cpupower 工具:
# 设置性能模式,禁用节能
cpupower frequency-set -g performance
cpupower idle-set -D 1
第一个命令把CPU调到最高频率,第二个命令禁止进入C1以上的节能状态。
4.5 知识体系总览
说了这么多,我画了一张图,帮你把整个体系串起来。你看一眼就明白了。
这张图把四个方法串起来了。绑核是基础,中断隔离是保障,关超线程是净化,关节能是稳定。四者缺一不可。
4.6 实战检查清单
最后,我整理了一份检查清单。每次部署交易服务器,我都会按这个走一遍:
- CPU拓扑确认:用
lscpu -e确认物理核和逻辑核分布 - 绑核配置:交易线程绑定到独占物理核,非关键线程绑定到其他核
- 中断隔离:在
/etc/default/grub中添加isolcpus参数 - 超线程检查:用
cat /sys/devices/system/cpu/smt/active确认关闭 - 节能状态检查:用
cpupower idle-info确认只有C0状态 - 延迟验证:用
cyclictest或自研工具测量延迟抖动
好了,这一章的内容就到这里。CPU亲和性与隔离,说白了就是给交易线程一个“专属VIP包间”,不让任何人打扰它。下一章我们会聊内存优化,那又是另一个精彩的话题。
公众号:蓝海资料掘金营,微信deep3321