在摩尔定律失效的情况下,CPU开始通过多核来提高性能,但这带来了一系列的问题,本系列的文章将介绍这些问题、在X86下目前的解决方案、以及我们应该如何更好的使用CPU。
一、CPU多核架构的演进
概念解释
因为目前的CPU都是多Core架构,CPU指i5、i7的处理器,一个处理器对应主板上的一个槽位(Socket);在一个处理器中会有多个物理核心(Core),每一个物理核心中都有独立的一套ALU、FPU、Cache等组件;一个核心一般对应一个逻辑核(Processor),但在开启超线程的情况下,会对应多个逻辑核,而操作系统调度的基本单位是逻辑核。
简单来说就是,当有多个计算任务时,可以让其中一个计算任务使用ALU的时候,另一个则去使用FPU。 这样就可以充分利用物理核中的各个部件,使得同一个物理核中,也可以并行处理多个计算任务。
我们可以在Linux上通过’lsCpu’看到这几个概念:
来自:lscpu命令详解 – 马昌伟 – 博客园 (cnblogs.com)
经典南桥北桥结构(总线型)
所有CPU Core通过公共总线(FSB)连接到北桥,北桥用于访问RAM,对于其他系统设备(SATA、USB、PCI-E)的IO访问,CPU Core需要经由北桥与南桥(也叫I/O桥)进行通信。
问题
这样的架构虽然简单,但是性能并不是随着CPU Core的增多而扩展的,因为Cores需要通过公共总线进行RAM等访问,而因为CPU与RAM的速度存在较大gap,这样整个系统的性能与吞吐受限于内存的带宽(因为L1、L2、L3 Cache是从内存中加载的),当内存带宽被打满时,即使再增加CPU Core也是无用的。
优化-处理器独占总线(DHSI,专用高速互联)
为了解决总线的瓶颈问题,那么每个CPU core独占一个总线在一定程度上缓解了问题;但是处理器独占总线一定程度上增加了维护多核数据一致性的成本,因为原本每个核只需要Snoop(嗅探)一条总线就行了,现在每个核需要Snoop多条总线,因此DHSI架构在芯片组上增加了Snoop Filter,每个处理器只处理自己关心的数据,架构如下图所示:
DHSI在一定程度上缓解了CPU增加对于总线的压力,但是没有缓解随着CPU增加而带来的内存的压力(CPU越多,需要的内存空间越多),所以在NUMA下进一步优化了CPU对于内存的访问方式。
QPI(QuickPath Interconnect,NUMA)
QPI相比于之前的架构,最主要的变化是内存控制器集成进了CPU Socket,不再由北桥负责,每个Socket都会有一个独立的内存控制器IMC(integrated memory controllers, 集成内存控制器),分属于不同的socket的IMC之间通过QPI link(interconnect)通讯
属于同一个Socket的core通过类似DHSI的总线进行通信,这条总线也叫做IMC bus:
此时物理架构从UMA演进为了NUMA;
- UMA: 统一内存地址访问,即对于所有CPU,CPU对于内存的访问方式都是相同的;
- NUMA: 非统一内存地址访问,即对于所有CPU,CPU对于内存的访问方式是不同的,有Local与Remote之分。
而Linux中NUMA默认的内存分配策略是,Socket上的CPU Core优先使用自身所在Socket内存控制器对应的内存,当自身内存不足时,再去其他内存控制器中申请内存,如果都没有再进行内存清理回收;
- Local Access: 如果CPU访问自身内存控制器对应的内存,速度较快(100ns),称为Local Access;
- Remote Access: 如果CPU访问其他CPU的内存控制器对应的内存,速度相对慢一些(160ns),称为Remote Access;
经过一系列的优化,Remote Access比Local Access的访问速度从慢7倍到现在只慢30%,目前是否Local Access已经不是着重需要考虑的点了;上述给出的访问ns数是基于QPI基本空闲(内存分配合理,跨Socket(Node)分配内存少)的情况下的测试结果。
性能参考:NUMA对性能的影响 – 知乎 (zhihu.com)
问题
NUMA架构使用多核CPU带来IPC(Instruction per cycle)的提升,缓解了内存总线带宽与多核CPU存在速度gap的问题(核越多,CPU计算需要的内存访问越多,问题越严重);但是也不可避免的加重了维护多核数据一致性的成本;
因此这些问题,我们需要先了解CPU在NUMA下是如何访问内存的,然后再看看在应用层面,可以有哪些优化方式。
本系列的下一篇文章将介绍CPU在NUMA下访问的整体流程。