一、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 --membind 或 mbind() 系统调用,把内存绑到正确的节点上。
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架构的层次关系。每一层都有坑,也都有优化空间。咱们后面几章会逐一深入。
核心总结:
- 缓存行对齐,避免伪共享
- NUMA系统上,线程和内存要绑在同一节点
- 超线程慎用,关键线程绑定物理核心
好了,这一章就到这里。记住,CPU架构不是理论,是实实在在影响你交易系统延迟和吞吐量的关键。下一章咱们聊具体的亲和性绑定策略,到时候见。
公众号:蓝海资料掘金营,微信deep3321