一、CPU架构基础:核心、缓存与NUMA

各位同学,咱们今天聊点硬核的。CPU架构这东西,说白了就是交易系统的“地基”。地基没打好,上层优化再花哨也是白搭。我个人习惯,做任何性能调优之前,先摸透CPU的脾气。

1.1 核心与缓存结构

先看最基础的——CPU核心。每个核心,你可以把它想象成一个独立的“工人”。工人干活需要工具,工具放哪儿?放缓存里。

缓存分三级:L1、L2、L3。L1最快,但容量最小,通常只有几十KB。L2稍慢,容量大一些,几百KB。L3最慢,但容量最大,几MB到几十MB。

我在项目中遇到过一个问题:某个策略在回测时跑得飞快,一上实盘就卡顿。查了半天,发现是数据在L1和L2之间频繁“搬家”,缓存命中率低得可怜。后来调整了数据结构,把热点数据对齐到缓存行,问题就解决了。

关键点:缓存行(Cache Line)通常是64字节。如果你频繁修改同一缓存行里的不同变量,就会引发“伪共享”(False Sharing),性能直接腰斩。

举个例子,两个线程分别修改结构体中的两个相邻字段:

// 伪共享的典型例子
struct Data {
    int a;  // 线程1修改
    int b;  // 线程2修改
};

嗯,这里要注意。a和b很可能在同一个缓存行里。线程1改了a,线程2的缓存行就失效了,哪怕它只关心b。解决办法?填充(padding)一下:

struct Data {
    int a;
    char padding[60]; // 填充到64字节
    int b;
};

你想想看,一个简单的填充,性能可能提升30%以上。这就是细节的力量。

1.2 NUMA与SMP架构区别

接下来聊NUMA和SMP。这两个概念,很多做交易系统的人容易忽略,但恰恰是延迟敏感型应用的关键。

SMP(对称多处理):所有核心共享一个内存控制器。访问任何内存地址,延迟都一样。听起来很公平,对吧?但问题在于,核心多了以后,内存总线会成为瓶颈。

NUMA(非统一内存访问):每个CPU有自己的本地内存。访问本地内存快,访问远程内存慢。延迟差异可能达到2-3倍。

为什么会这样?说白了,物理距离决定了延迟。你想想看,数据从隔壁CPU的内存拿,总得绕一圈吧?

特性 SMP NUMA
内存访问延迟 统一 非统一(本地快,远程慢)
扩展性 差(核心数受限) 好(可支持上百核心)
适用场景 小规模系统 大规模、高性能计算

我曾经在一个8路服务器上做交易系统优化。一开始没注意NUMA,结果发现线程被调度到了不同的NUMA节点上,内存访问延迟忽高忽低。后来强制绑定CPU亲和性,把线程和内存都固定在同一个节点上,延迟直接降了40%。

避坑指南:我曾经在NUMA系统上犯过一个低级错误——分配内存时没指定节点。结果线程在节点0上跑,内存却分配到了节点1。每次访问都要跨节点,性能惨不忍睹。记住:用 numactl --membindmbind() 系统调用,把内存绑到正确的节点上。

1.3 超线程技术原理

超线程(Hyper-Threading),Intel的叫法。AMD那边叫SMT(同步多线程)。原理很简单:一个物理核心,模拟成两个逻辑核心。

怎么做到的?每个物理核心内部,其实有多个执行单元。比如整数运算单元、浮点运算单元、加载存储单元。一个线程往往只用其中一部分,剩下的就浪费了。超线程就是让两个线程共享这些执行单元,提高利用率。

但注意,这不是免费的午餐。两个逻辑核心共享L1和L2缓存,也共享前端解码器。如果两个线程争抢资源,性能反而会下降。

我个人的经验是:交易系统里,慎用超线程。尤其是高频交易场景,延迟敏感,你根本不想跟别人抢资源。我习惯把超线程关掉,或者至少把关键线程绑定到物理核心上,避开逻辑核心。

小技巧:在Linux上,可以用 lscpu -e 查看核心拓扑。物理核心和逻辑核心通常有对应关系。比如CPU0和CPU1是同一个物理核心的两个逻辑核心。绑定线程时,尽量用CPU0、CPU2、CPU4这样的物理核心,避开CPU1、CPU3、CPU5。

来看一个简单的绑定示例:

// 绑定线程到CPU0(物理核心)
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset);
pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);

嗯,代码很简单,但效果立竿见影。我见过太多人忽略这一步,结果线程在核心之间跳来跳去,缓存频繁失效,延迟抖动严重。

知识体系总览

下面这张图,把咱们刚才聊的内容串起来。你可以看到,从物理核心到缓存结构,再到NUMA和超线程,每一层都影响着交易系统的性能。

CPU架构知识体系 物理核心(Physical Core) 每个核心独立执行指令,拥有私有L1/L2缓存 缓存结构(Cache Hierarchy) L1(最快,32KB)→ L2(较快,256KB)→ L3(较慢,数MB) 缓存行64字节,伪共享是常见陷阱 架构类型:SMP vs NUMA SMP:统一内存访问,扩展性差 NUMA:本地内存快,远程内存慢,需绑定亲和性 超线程(Hyper-Threading / SMT)

这张图从上到下,展示了CPU架构的层次关系。每一层都有坑,也都有优化空间。咱们后面几章会逐一深入。

核心总结:

  • 缓存行对齐,避免伪共享
  • NUMA系统上,线程和内存要绑在同一节点
  • 超线程慎用,关键线程绑定物理核心

好了,这一章就到这里。记住,CPU架构不是理论,是实实在在影响你交易系统延迟和吞吐量的关键。下一章咱们聊具体的亲和性绑定策略,到时候见。


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