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就不会收到任何中断请求。交易线程可以安心跑,没人打扰。

避坑指南:我曾经把系统关键中断(比如时钟中断)也隔离了,结果系统直接hang住。记住,至少留一个核给操作系统处理中断和调度。

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以上的节能状态。

我的建议:交易服务器就别想着省电了。省那点电费,不够一次延迟超标赔的。直接BIOS里把“Performance”模式拉满,C-states全关。

4.5 知识体系总览

说了这么多,我画了一张图,帮你把整个体系串起来。你看一眼就明白了。

CPU亲和性与隔离核心方法 1. 绑核 • 线程钉死在物理核上 • 避免缓存失效和TLB重载 • 使用pthread_setaffinity_np() • 区分物理核与逻辑核 • 关键:独占且不迁移 2. 中断隔离 • 隔离CPU核免受中断打扰 • 使用isolcpus内核参数 • 配置/proc/irq/亲和性 • 配合nohz_full和rcu_nocbs • 关键:留核给系统 3. 关闭超线程 • 逻辑核共享物理核资源 • 缓存冲突导致延迟抖动 • BIOS或内核参数关闭 • 平均延迟降25%+ • 关键:99.9%延迟降5倍 4. 关闭节能状态 • C-states导致唤醒延迟 • P-states导致频率波动 • BIOS关闭或cpupower设置 • 强制C0 + performance • 关键:唤醒延迟可达50μs 稳定低延迟

这张图把四个方法串起来了。绑核是基础,中断隔离是保障,关超线程是净化,关节能是稳定。四者缺一不可。

4.6 实战检查清单

最后,我整理了一份检查清单。每次部署交易服务器,我都会按这个走一遍:

  1. CPU拓扑确认:用 lscpu -e 确认物理核和逻辑核分布
  2. 绑核配置:交易线程绑定到独占物理核,非关键线程绑定到其他核
  3. 中断隔离:在 /etc/default/grub 中添加 isolcpus 参数
  4. 超线程检查:用 cat /sys/devices/system/cpu/smt/active 确认关闭
  5. 节能状态检查:用 cpupower idle-info 确认只有C0状态
  6. 延迟验证:用 cyclictest 或自研工具测量延迟抖动
最后提醒一句:这些优化做完后,一定要做压力测试。我曾经在某个项目里,把所有优化都上了,结果发现网卡驱动和中断隔离有冲突,导致丢包。所以,优化完一定要跑全链路压测,确认没问题再上线。

好了,这一章的内容就到这里。CPU亲和性与隔离,说白了就是给交易线程一个“专属VIP包间”,不让任何人打扰它。下一章我们会聊内存优化,那又是另一个精彩的话题。


公众号:蓝海资料掘金营,微信deep3321