【优化技术专题】这绝对是你的知识盲点,NUMA的为什么存在

995 阅读6分钟

小知识,大挑战!本文正在参与「程序员必备小知识」创作活动

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。

NUMA的产生背景

在NUMA架构出现前,CPU欢快的朝着频率越来越高的方向发展(纵向发展)。受到物理极限的挑战又转为核数越来越多的方向发展(横向发展)。在一开始,内存控制器还在北桥中,所有CPU对内存的访问都要通过北桥来完成。此时所有CPU访问内存都是“一致的”,如下图所示:

这样的架构称为UMA(Uniform Memory Access),直译为“统一内存访问”,这样的架构对软件层面来说非常容易,总线模型保证所有的内存访问是一致的,即每个处理器核心共享相同的内存地址空间但随着CPU核心数的增加,这样的架构难免遇到问题,比如对总线的带宽带来挑战、访问同一块内存的冲突问题。为了解决这些问题,有人搞出了NUMA

注意:北桥芯片(North Bridge)是主板芯片组中起主导作用的最重要的组成部分,也称为主桥(Host Bridge)。 一般来说, 芯片组的名称就是以北桥芯片的名称来命名的,例如:英特尔845E芯片组的北桥芯片是82845E,875P芯片组的北桥芯片是82875P等等。


NUMA构架细节

通过“NUMA的产生背景”,让我们了解到了之前内存访问架构原则,与此相对的就是NUMANUMA 全称 Non-Uniform Memory Access,译为“非一致性内存访问”。这种构架下,不同的内存器件和CPU核心从属不同的Node,每个 Node 都有自己的集成内存控制器IMC,Integrated Memory Controller)。

在 Node 内部,架构类似SMP,使用IMC Bus进行不同核心间的通信;不同的 Node间通过QPIQuick Path Interconnect)进行通信,如下图所示:

一般来说,一个内存插槽对应一个Node。需要注意的一个特点是,QPI的延迟要高于IMC Bus,也就是说CPU访问内存有了远近(remote/local)之别,而且实验分析来看,这个差别非常明显。

在Linux中,对于NUMA有以下几个需要注意的地方:

  • 默认情况下,内核不会将内存页面从一个 NUMA Node 迁移到另外一个 NUMA Node;
  • 但是有现成的工具可以实现将冷页面迁移到远程(Remote)的节点:NUMA Balancing;
  • 关于不同 NUMA Node 上内存页面迁移的规则,社区中有依然有不少争论。

NUMA详细分析

非一致性内存架构(Non-uniform Memory Architecture)是为了解决传统的对称多处理(Symmetric Multi-processor)系统中的可扩展性问题而诞生的。在对称多处理系统中,处理器共享北桥中的内存控制器来达到共同访问外部内存和IO的目的,也就是说所有的处理器对内存和I/O的访问方式和开销都是相同的。在这种系统中,随着更多的处理器被添加到SMP系统中,总线的竞争将会越来越大,系统的性能也必将随之大打折扣。SMP系统的示意图如下:

  • 本地节点: 对于某个节点中的所有CPU,此节点称为本地节点;(速度最快)
  • 邻居节点: 与本地节点相邻的节点称为邻居节点;(速度次之)
  • 远端节点: 非本地节点或邻居节点的节点,称为远端节点。(速度最差)

超立方体可以作为一种有效的拓扑来描述NUMA系统,它将系统中的节点数限制在2^C内,C是每个节点拥有的邻居节点数,如下图所示

开销总结

以C=3为例,则对于节点1而言,2,3,5则为邻居节点,4,6,7,8为远端节点,显然访问开销的关系为 本地节点<邻居节点<远端节点。


NUMA案例分析

AMD Hyper-Transport

早期的SMP(对称多处理器系统) 只拥有一个位于北桥中的内存控制器,而如今更先进的做法是将内存控制器整合到CPU中去,这样每个CPU都拥有自己的内存控制器,不会相互之间产生竞争

最先采用这种做法的一批处理器就是AMD在2003年推出的AMD Opteron系列处理器,其结构如下图所示

每个CPU中都整合了一个内存控制器(IMC),并且CPU之间采用了一种Hyper-Transport的技术建立连接,这种连接可以使得CPU通过其他CPU来访问外部内存,当然访问开销要比访问本地内存更大

操作系统的支持

为了支持NUMA架构,OS的设计必须将内存分布的特点考虑进去

举一个简单的例子,假如一个进程运行在一个给定的处理器中,那么为这个进程所分配的物理内存就应该是该处理器的本地内存,而不是外部内存

  1. OS还要注意避免将一个进程从一个节点给迁移到另一个节点。在一个普通的多处理系统中,OS就应该已经尝试不去在处理器之间迁移进程,因为这意味着一个处理器的cache中的相关内容都将被丢失如果在某种情况下必须进行迁移,那么OS可以随意选择一个空闲的处理器。

  2. 但是在NUMA系统中,可选择的新处理器将要受到一些限制,最重要的一点就是新处理器访问内存的开销不能比先前的处理器大,也就是说应该尽可能选择本地节点中的处理器。

  3. 当找不到符合条件的处理器,OS才能选择其他类型的处理器

  4. 在这种较糟的情况下有两种选择

    • 如果进程只是暂时性的被迁移出去,可以再将其迁移回更加合适的处理器;
    • 如果不是暂时性的,那么可以将该进程的内存拷贝到新处理器的内存中,这样就可以通过访问拷贝的内存来消除访问外部内存的开销,显然这是一种空间换时间的做法。

NUMA Node 操作

NUMA Node 分配

有两个NUMA Node,每个节点管理16GB内存。

NUMA Node 绑定

Node 和 Node 之间进行通信的代价是不等的,同样是 Remote 节点,其代价可能不一样,这个信息在 node distances 中以一个矩阵的方式展现。

我们可以将一个进程绑定在某个 CPU 或 NUMA Node 的内存上执行,如上图所示。

NUMA 状态