第一章:CPU架构基础——NUMA与SMP、缓存层级与核心识别

各位同学好,我是老张。在量化交易系统里摸爬滚打了十几年,我见过太多因为CPU调度不当导致的延迟抖动。今天咱们聊的这些东西,说白了就是为后续的亲和性绑定打地基。地基不牢,后面全白搭。

先问大家一个问题:你写了一个多线程程序,跑在服务器上,结果发现某些线程就是比别的慢。你查了代码,查了锁,都没问题。最后发现——哦,线程被调度到了不同的CPU核心上,有的核心离内存远,有的离内存近。这就是NUMA架构在搞鬼。

核心观点:不理解CPU架构,就别谈性能优化。尤其是做量化交易,微秒级的抖动都可能让你错过行情。

1.1 SMP与NUMA:两种截然不同的内存访问模型

SMP(对称多处理),这是早期的多CPU方案。所有CPU共享一条内存总线,访问任何内存地址的延迟都一样。听起来很公平对吧?但问题在于——总线带宽有限。CPU一多,大家抢总线,性能直线下降。我在2010年第一次调优一个8路服务器时,就吃过这个亏。当时以为加CPU就能线性提升性能,结果发现总线成了瓶颈。

NUMA(非统一内存访问),这是现代服务器的标配。每个CPU有自己的本地内存,访问本地内存快,访问远端内存慢。为什么会这样?说白了,就是为了解决SMP的总线瓶颈。每个CPU节点独立运行,通过高速互联通道(比如Intel的UPI、AMD的Infinity Fabric)通信。

特性 SMP NUMA
内存访问延迟 统一 本地快、远端慢
扩展性 差(8路以上困难) 好(可扩展到数百核心)
典型场景 嵌入式、低端服务器 高性能计算、交易系统
编程复杂度 高(需考虑内存亲和性)

我个人习惯,拿到一台新服务器,第一件事就是跑 numactl --hardware 看看NUMA拓扑。你想想看,如果你的交易线程跑在Node 0上,但分配的内存却在Node 1上,每次内存访问都要跨节点,延迟能不高吗?

避坑指南:我曾经遇到一个案例,某团队把行情网关和交易引擎部署在同一台机器上,结果行情网关的内存分配在Node 0,交易引擎的线程却跑在Node 1。每次行情数据过来,交易引擎都要跨节点读取,延迟直接翻倍。后来绑定了亲和性,问题解决。

1.2 L1/L2/L3缓存层级:为什么数据局部性如此重要

CPU的缓存层级,我习惯把它想象成一个金字塔:

  • L1缓存:每个核心独享,32KB-64KB,延迟约1ns。分指令缓存和数据缓存。
  • L2缓存:每个核心独享,256KB-512KB,延迟约3-5ns。
  • L3缓存:所有核心共享,几MB到几十MB,延迟约10-20ns。
  • 主存:所有核心共享,延迟约100ns+。

嗯,这里要注意:L3缓存虽然是共享的,但在NUMA架构下,每个节点有自己的L3。跨节点访问L3,其实比访问本地主存还慢。我见过有人把热点数据放在远端L3上,结果性能还不如直接读本地内存。

为什么缓存对交易系统这么重要?因为你的订单簿、行情数据、策略参数,这些热点数据如果能全部塞进L2甚至L1缓存里,延迟能降低一个数量级。我记得有一次优化一个做市商策略,把核心数据结构从链表改成数组,配合缓存行对齐,延迟从5微秒降到了1.2微秒。

关键技巧:lscpu -C 查看每个核心的缓存大小。如果你的热点数据超过L2大小,就要考虑分片或者重新设计数据结构了。

1.3 超线程与物理核心:别被操作系统骗了

超线程(Hyper-Threading),说白了就是一个物理核心模拟成两个逻辑核心。操作系统看到的是两个CPU,但实际上它们共享执行单元、L1和L2缓存。这意味着什么?两个逻辑核心不能同时全速运行,它们会互相争抢资源。

我建议,做交易系统时,永远不要把两个关键线程放在同一个物理核心的两个超线程上。为什么?因为一旦其中一个线程发生缓存未命中,另一个线程也会被拖慢。我曾经在回测系统里犯过这个错,两个计算密集型的线程跑在同一个物理核心上,结果性能反而比单线程还差。

怎么识别物理核心和逻辑核心?用 lscpu 命令:

# 查看CPU拓扑
lscpu -e

# 输出示例:
# CPU NODE SOCKET CORE L1d:L1i:L2:L3 ONLINE
# 0   0    0      0    0:0:0:0       yes
# 1   0    0      0    0:0:0:0       yes  ← 这是超线程
# 2   0    0      1    1:1:1:0       yes
# 3   0    0      1    1:1:1:0       yes  ← 这也是超线程

看到没?CPU 0和CPU 1共享同一个CORE 0。如果你把两个线程绑到CPU 0和CPU 1上,它们其实在打架。正确的做法是绑到CPU 0和CPU 2上,这样它们在不同的物理核心上。

警告:有些云服务器会隐藏真实的CPU拓扑信息。如果你在虚拟化环境里跑交易系统,一定要确认宿主机是否透传了NUMA信息。否则你绑定的亲和性可能是假的。

1.4 知识体系总览

下面这张图,是我自己总结的CPU架构知识体系。每次给团队做培训,我都会先画这张图。你把它理解了,后面的亲和性绑定技巧就水到渠成。

CPU架构知识体系 SMP对称多处理 NUMA非统一内存访问 缓存层级 L1 (1ns) L2 (3-5ns) L3 (10-20ns) 主存 (100ns+) 核心识别 物理核心 (Core) 逻辑核心 (HT) lscpu -e 查看 目标:理解架构 → 识别核心 → 绑定亲和性 → 降低延迟

这张图从左到右,从上到下,就是咱们这节课的脉络。先搞清楚你的服务器是SMP还是NUMA,再摸清缓存层级,最后学会识别物理核心和超线程。每一步都踩实了,后面讲亲和性绑定的时候,你才能理解为什么这么绑、绑了有什么效果。

个人经验:我每次接手一个新交易系统,第一周什么都不干,就研究CPU拓扑。用 lstopo 生成可视化的拓扑图,贴在工位上。这样写代码的时候,脑子里就有个地图,知道数据该放哪、线程该跑哪。

好了,这一章就到这里。记住:CPU架构不是理论,是实战。下次你写多线程代码的时候,多想想你的数据在哪个缓存里、你的线程在哪个核心上。这些细节,决定了你的交易系统是赚是赔。


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