基本上是对网上资料进行的整理。
1.NUMA简介
1.1为什么需要NUMA
CPU朝着高频方向发展遇到了天花板,转而向着多核心方向发展,由于在一致性内存访问(UMA——Uniform Memory Access)架构中,所有CPU对内存的访问都要通过总线完成,而总线发展缓慢,导致多核CPU通过一条总线共享内存成为瓶颈(总线带宽、访问同一块内存的冲突问题等)。
1.2什么是NUMA
NUMA全称Non-Uniform Memory Access(非一致性内存访问)。不同的内存器件和CPU核心从属于不同的Node,,每个Node都有自己的集成内存控制器(IMC,Integrated Memory Controller)。
而在Node内部,其架构类似于UMA,几个核心共享该Node的内存,并且通过IMC Bus进行不同核心之间的通信;不同Node之间则通过QPI(Quick Path Interconnect——又名CSI,Common System Interface公共系统接口,是一种可以实现芯片间直接互联的架构)进行通信。
1.3NUMA的特点
三个节点的概念:
(1)本地节点:对于某个节点中的所有CPU,此节点称为本地节点。
(2)邻居节点:与本地节点相邻的节点称为邻居节点。
(3)远端节点:非本地节点或邻居节点的节点,称为远端节点。
本地节点内,CPU核心按照UMA共享内存架构通过bus共享对内存模块的访问,而本地节点内CPU则通过inter-connect访问非本地节点内存。
注意:inter-connect延迟高于bus。
因此,CPU访问内存有远近之分,分别称为Remote Access和Local Access。访问速度与节点的距离有关,距离越远访问速度越慢,此距离称作Node Distance。
在Linux中,对于NUMA需要注意的地方:
(1)默认情况下,内核不会将内存页面从一个 NUMA Node 迁移到另外一个 NUMA Node;
(2)但是有现成的工具可以实现将冷页面迁移到远程(Remote)的节点:NUMA Balancing;
(3)关于不同 NUMA Node 上内存页面迁移的规则,社区中有依然有不少争论;
(4)Linux识别到NUMA架构后,默认的内存分配方案就是:优先尝试在请求线程当前所处的CPU的Local内存上分配空间。如果local内存不足,优先淘汰local内存中无用的Page(Inactive,Unmapped)。
2.NUMA带来的问题和可能的解决方案
2.1NUMA带来的问题
(1)CPU亲和策略导致的内存分配不平均;
(2)NUMA Zone Claim内存回收导致大量有用内存被回收;
(3)操作系统的调度程序可能会将线程从一个内核迁移到另一个内核,导致线程与其原本本地内存之间的关联断开,由本地访问变成远程访问,使得内存访问时间大幅增加。
原文:
复杂操作系统主要使用调度程序将应用线程分配给处理器内核。 调度程序考虑系统状态和不同的策略目标(比如“平衡内核负载”或“整合内核上的线程或使内核保持为休眠状态”),然后匹配应用线程和相应的物理内核。 特定线程会在其分配的内核上执行一段时间,之后被交换到内核之外进行等待,因为其他线程也需要执行。 如果另一内核可用,调度程序将选择迁移该线程,以确保及时执行并实现其策略目标。
将线程从一个内核迁移到另一内核会导致 NUMA 共享内存架构出现问题,因为它会断开线程与其本地内存分配之间的关联。 也就是说,线程可能启动时在节点 1 上分配内存,因为它在节点 1 封装中的内核上运行。 但是当该线程后来迁移至节点 2 上的内核时,之前保存的数据将变成远程数据,且内存访问时间也会大幅增加。
输入处理器关联。 处理器关联指线程/进程与特定处理器资源实例相关联的持续性(无论其他实例的可用性如何)。 通过使用系统 API,或修改操作系统数据结构(比如关联掩码),特定内核或内核集可与应用线程相关联。 然后在制定有关线程寿命的决策时,调度程序会注意到这种关联。 例如,线程可能配置成仅在内核 0-3(均属于四核 CPU 封装 0)上运行。 调度程序将在内核 0-3 之间进行选择,不会考虑将线程迁移至其他封装。
执行处理器关联可确保内存分配对有需要的线程保持本地性。 不过,也应该注意其存在的缺点。 一般来说,如果本可以使用更好的资源管理方式,处理器关联将会限制调度程序的选择,并产生资源争用现象,从而对系统性能造成不利影响。 除了阻止调度程序将等待线程分配给未利用的内核外,处理器关联的局限性还会对应用本身产生不利影响,因为其他节点上的额外执行时间无法弥补速度较慢的内存访问。
编程人员必须慎重考虑处理器关联方法是否适用于特定应用和共享系统环境。 最后需注意的一点是,除显式指令外,部分系统提供的处理器关联 API 还支持向调度程序提供优先级“提示”和关联“建议”。 相比于强制执行明确的线程放置结构,使用此类建议能够确保在通用案例中实现最佳性能,并在高资源竞争期间避免限制调度选择。
2.2可能的解决方案
NUMA的优化方式是:将内存通过特定node分配,并且让程序尽量快的读取这段内存。实现这种优化的首要方法是,线程通过node分配本地内存,并且保证这个线程一直工作在这个node上(通过node affinity来确定)。这将会获得最低的时延,最小的内部互联开销。
确定是否能够实现 NUMA 架构的性能优势,关键问题在于数据放置。 将数据有效放置在处理器(需要访问数据)本地内存的频率越高,该架构将越有利于缩短整体访问时间。
(1)NUMA Interleave
将内存page随机打散到各个CPU Core上,使得每个CPU的负载和Remote Access的出现频率都均匀分布
(2)Dynamic Memory Relocation
那是否能在Interleave模式下,把那些明显应该聚集在一个CPU上的内存集中在一起呢?要做到按照线程的行为动态的调整page在memory的分布,就势必需要做线程和内存的实时监控(profile)。对于Memory Access这种非常异常频繁的底层操作来说增加profile入口的性能损耗是极大的, 这个道理和为什么Linux没有全局监控CPU L1/L2 Cache命中率工具的原因是一样的。
(3)Page Replication
Page Replication在数据一致性上维护的成本显得比他带来的提升更多。因此这种尝试也仅仅停留在理论阶段。当然,如果能得到底层的大力支持,相信这个方案还是有极大的实际价值的。
(4)处理器关联
绑定线程/进程到指定的节点,能够确保内存分配对有需要的线程保持本地性,但是这种方法可能会导致负载不均衡对性能造成影响。
绑定线程/进程到指定的节点,能够确保内存分配对有需要的线程保持本地性,但是这种方法会限制调度程序的选择并产生资源争用现象,从而对系统性能造成不利影响。 除了阻止调度程序将等待线程分配给未利用的内核外,处理器关联的局限性还会对应用本身产生不利影响,因为其他节点上的额外执行时间无法弥补速度较慢的内存访问(自:可能需要编程人员自己设定保持负载平衡)。
除显式指令外,部分系统提供的处理器关联 API 还支持向调度程序提供优先级“提示”和关联“建议”。
(5)基于隐式内存分配策略的数据放置
操作系统以透明方式支持NUMA友好型数据放置。两个策略:首次访问本地性和首个请求本地性。
不需要编程人员明确指定数据应该放置在哪,但需要编程人员考虑访问数据的顺序,并根据这个顺序给出“提示”执行内存分配。
原文:
在简单情况下,许多操作系统以透明的方式支持 NUMA 友好型数据放置。 当单线程应用分配内存时,处理器会将内存页分配给与请求线程的节点(CPU 封装)相关的物理内存,从而确保其对线程的本地性并达到最佳的访问性能。
另外,部分操作系统会等待第一次内存访问完成,然后才执行内存页分配。 为了解此处所具备的优势,可以考虑带有启动顺序的多线程应用,即包含按照主控制线程的内存,然后是创建不同 worker 线程,然后是长时间应用处理或服务。 尽管将内存页放在分配线程的本地比较合理,但事实上,将其放在将访问数据的 worker 线程本地更加高效。 这样操作系统会注意到第一个访问请求,并根据请求程序的节点位置分配内存页。
这两种策略(首次访问本地性和首个请求本地性)共同证明,应用编程人员部署程序时必须了解 NUMA 环境。 如果内存页放置策略以首次访问为基础,编程人员可通过在启动时纳入精心设计的数据访问顺序,向操作系统生成有关最佳内存放置的“提示”,从而充分发挥这一策略的作用。 如果内存页放置策略以请求程序位置为基础,编程人员应确保由按照顺序访问数据的线程执行内存分配,而不是由设计为供应代理的初始化或控制线程执行。
访问相同数据的多个线程最好放在相同节点上,以便放置在节点本地的内存分配能够有利于所有线程。 例如,这样可用于预取方案,旨在通过在实际需要之前生成数据请求,以提升应用性能。 此类线程必须生成位于 NUMA 架构实际消费者线程本地的数据放置,以提升其特征性能。
需要注意的是,当操作系统充分使用某一节点的物理内存资源时,同一节点上的线程发来的内存请求将通常由远程节点上不具备最佳性能的分配来执行。 对于需要消耗大量内存的应用来说,这样能够正确地按照大小分类特定线程的内存需求,并确保与访问线程相关的本地放置。
在大量线程通过所有节点随机共享相同数据池的情况下,建议跨所有节点将数据划分成均匀的条带。 这样能够分散内存访问负载,避免系统中单个节点出现瓶颈访问模式。
(6)基于显式内存分配指令的数据放置
使用系统API明确配置内存页分配的位置,如Linux的libnuma库。
原文:
在基于 NUMA 的系统中,进行数据放置的另外一种方法是使用系统 API 明确配置内存页分配的位置。 其中一种 API 是面向 Linux 的 libnuma 库。
使用 API,编程人员能够建立虚拟内存地址范围与特定节点的关联,还可在轻松指示内存分配系统调用中的所需节点。 借助这种功能,应用编程人员可确保特定数据集的放置,无论由哪个线程分配或哪个线程首先访问。 例如,这样有利于复杂应用使用代表 worker 线程的内存管理线程的方案。 还证明了它适用于创建多个短期线程(均包含可预测数据要求)的应用。 预取方案也可大大受益于这种控制。
当然,这种方法也存在缺点,即应用编程人员在处理内存分配和数据放置时需完成大量管理任务。 错误放置的数据对性能的影响要甚于默认系统行为。 显式内存管理还假定整个应用使用期间处理器关联均处于精细控制之下。
基于 NUMA 的内存管理 API 为应用编程人员提供的另一项功能是内存页迁移。 通常来说,在节点之间迁移内存页是一项非常昂贵的操作,有时需要避免。 说到这点,倘若应用为长期、内存密集型应用,迁移内存页以重新建立 NUMA 友好型配置将物有所值。3 例如,可以试想一下长期应用,它包含多个已经终止的线程以及已经创建但驻留在另一节点上的新线程。 现在,对于需要它的线程来说,数据不再具有本地性,而占据主导地位的是性能较低的访问请求。 编程人员可以运用有关线程寿命和数据需求的特定于应用的知识,确定显式迁移是否井然有序。
3.工具
3.1numactl工具
安装:
centos:sudo yum install numactl -y
ubuntu(银河麒麟kylin):sudo apt-get install numactl
numactl是一个命令行工具,能够以特定的NUMA策略启动进程。可以在不修改或重新编译程序的前提下,设置NUMA策略。
(1)查看NUMA node信息——numactl --hardware
available:node列表
node X cpus:nodeX的核列表
node X size:nodeX总的内存大小
node X free:nodeX空闲内存大小
node distances:node之间的距离,由矩阵表示
(2)查看NUMA绑定信息——numactl --show
(3)查看NUMA统计信息
/sys/devices/system/node/文件夹中记录系统中的所有内存节点的相关详细信息。
numastat 等同于 cat /sys/devices/system/node/node[0…n]/numastat。
注:如果发现 numa_miss 数值比较高时,说明需要对分配策略进行调整。例如将指定进程关联绑定到指定的CPU上,从而提高内存命中率。
(4)nodes和cpus的表示方法
在CPU和内存分配中需要用到这些表示方法:
(5)CPU分配策略
每个进程或线程都会从父进程继承NUMA CPU分配策略,并且分配有一个优先node。
--all或者-a:
重置所有的CPU分配策略。进程可以使用所有可能的核。
--cpunodebind=nodes或者-N nodes或者--cpubind=nodes:
规定进程在指定的NUMA node上运行。
--physcpubind=cpus或者-C cpus:
规定进程在指定的CPU核上运行。
(6)内存分配策略
--localalloc或者-l:
规定进程从本地节点上请求分配内存。
--membind=nodes或者-m nodes
规定进程只能从指定的nodes上请求分配内存。
--preferred=node:
指定一个推荐的node来获取内存,如果获取失败,则尝试别的node。
--interleave=nodes或者-i nodes:
规定进程从指定的nodes上,以round robin算法交织地请求内存分配。
标注:
轮询调度(Round-Robin Scheduling)
轮询调度(Round Robin Scheduling)算法就是以轮询的方式依次将请求调度不同的服务器,即每次调度执行i = (i + 1) mod n,并选出第i台服务器。算法的优点是其简洁性,它无需记录当前所有连接的状态,所以它是一种无状态调度。
轮询调度算法的原理是每一次把来自用户的请求轮流分配给内部中的服务器,从1开始,直到N(内部服务器个数),然后重新开始循环。
参考:
//www.greatytc.com/p/f51b309f3a07
http://cenalulu.github.io/linux/numa/
//www.greatytc.com/p/0607c5f62c51
https://blog.csdn.net/wylfengyujiancheng/article/details/85417675
https://software.intel.com/content/www/cn/zh/develop/articles/optimizing-applications-for-numa.html