NUMA 内存性能

NUMA 局部性

某些平台可能在计算节点上附加多种类型的内存。这些不同的内存范围可能共享某些特性,例如 CPU 缓存一致性,但可能具有不同的性能。例如,不同的介质类型和总线会影响带宽和延迟。

系统通过基于局部性和性能特性将每种内存类型分组到不同的域或“节点”下来支持这种异构内存。某些内存可能与 CPU 共享同一个节点,而另一些内存则仅作为内存节点提供。虽然仅内存节点不提供 CPU,但它们相对于其他节点而言,可能仍然是本地的到一个或多个计算节点。下图显示了两个计算节点各自具有本地内存和一个仅内存节点的示例,每个计算节点都有一个仅内存节点。

+------------------+     +------------------+
| Compute Node 0   +-----+ Compute Node 1   |
| Local Node0 Mem  |     | Local Node1 Mem  |
+--------+---------+     +--------+---------+
         |                        |
+--------+---------+     +--------+---------+
| Slower Node2 Mem |     | Slower Node3 Mem |
+------------------+     +--------+---------+

“内存启动器”是包含一个或多个设备(例如 CPU 或单独的内存 I/O 设备)的节点,这些设备可以启动内存请求。“内存目标”是包含一个或多个可从一个或多个内存启动器访问的物理地址范围的节点。

当存在多个内存启动器时,它们在访问给定内存目标时可能并非都具有相同的性能。每个启动器-目标对可以组织成不同的排序访问类来表示这种关系。给定目标的性能最高的启动器被认为是该目标的本地启动器之一,并被赋予最高的访问类 0。任何给定目标都可能有一个或多个本地启动器,并且任何给定启动器都可能具有多个本地内存目标。

为了帮助应用程序将其内存目标与其启动器匹配,内核提供了彼此的符号链接。以下示例列出了访问类“0”内存启动器和目标的关系

# symlinks -v /sys/devices/system/node/nodeX/access0/targets/
relative: /sys/devices/system/node/nodeX/access0/targets/nodeY -> ../../nodeY

# symlinks -v /sys/devices/system/node/nodeY/access0/initiators/
relative: /sys/devices/system/node/nodeY/access0/initiators/nodeX -> ../../nodeX

内存启动器可能在同一访问类中具有多个内存目标。给定类中目标内存的启动器表示节点的访问特性相对于其他链接的启动器节点共享相同的性能。但是,启动器访问类中的每个目标彼此之间不一定具有相同的性能。

访问类“1”用于区分作为 CPU 的启动器,因此适用于通用任务调度,以及 IO 启动器(例如 GPU 和 NIC)。与访问类 0 不同,仅考虑包含 CPU 的节点。

NUMA 性能

应用程序可能希望根据节点的性能特性来考虑从哪个节点分配内存。如果系统提供这些属性,则内核会通过在内存节点的访问类 0 启动器下附加属性目录,将其导出到节点 sysfs 层次结构下,如下所示

/sys/devices/system/node/nodeY/access0/initiators/

这些属性仅在从该访问的启动器下链接的节点访问时才适用。

内核为本地启动器提供的性能特征导出如下

# tree -P "read*|write*" /sys/devices/system/node/nodeY/access0/initiators/
/sys/devices/system/node/nodeY/access0/initiators/
|-- read_bandwidth
|-- read_latency
|-- write_bandwidth
`-- write_latency

带宽属性以 MiB/秒为单位提供。

延迟属性以纳秒为单位提供。

此处报告的值对应于平台的额定延迟和带宽。

访问类 1 采用相同的形式,但仅包括 CPU 到内存活动的数值。

NUMA 缓存

系统内存可以以具有各种性能特性的元素层级结构构建,以便提供由较小的更高性能内存缓存的较大地址空间的较慢性能内存。系统物理地址内存启动器所知道的是由层级结构中的最后一个内存级别提供的。同时,系统使用更高性能的内存来透明地缓存对逐渐变慢级别的访问。

术语“远内存”用于表示层级结构中的最后一级内存。每个增加的缓存级别都提供更高的性能启动器访问,术语“近内存”表示系统提供的最快缓存。

此编号与 CPU 缓存不同,在 CPU 缓存中,缓存级别(例如:L1、L2、L3)使用 CPU 侧视图,其中每个增加的级别都具有较低的性能。相反,内存缓存级别以最后一个级别内存为中心,因此编号较高的缓存级别对应于更靠近 CPU 的内存,并且远离远内存。

内存侧缓存无法通过软件直接寻址。当软件访问系统地址时,如果存在,系统将从近内存缓存中返回该地址。如果不存在,则系统将访问下一级内存,直到该缓存级别命中或到达远内存为止。

应用程序无需知道缓存属性即可使用系统。软件可以选择查询内存缓存属性,以便最大化此类设置的性能。如果系统为内核提供了一种发现此信息的方式,例如使用 ACPI HMAT(异构内存属性表),则内核会将这些属性附加到 NUMA 节点内存目标。

当内核首次向节点注册内存缓存时,内核将创建以下目录

/sys/devices/system/node/nodeX/memory_side_cache/

如果该目录不存在,则系统要么不提供内存侧缓存,要么内核无法访问该信息。

每个级别的缓存的属性都在其缓存级别索引下提供

/sys/devices/system/node/nodeX/memory_side_cache/indexA/
/sys/devices/system/node/nodeX/memory_side_cache/indexB/
/sys/devices/system/node/nodeX/memory_side_cache/indexC/

每个缓存级别的目录都提供其属性。例如,以下显示了一个缓存级别以及可供软件查询的属性

# tree /sys/devices/system/node/node0/memory_side_cache/
/sys/devices/system/node/node0/memory_side_cache/
|-- index1
|   |-- indexing
|   |-- line_size
|   |-- size
|   `-- write_policy

如果它是直接映射缓存,则“索引”将为 0;对于任何其他基于索引的多路关联性,则为非零。

“line_size”是从缓存未命中时从下一缓存级别访问的字节数。

“size”是此缓存级别提供的字节数。

“write_policy”对于写回缓存将为 0,对于直写缓存将为非零。

另请参阅

[1] https://www.uefi.org/sites/default/files/resources/ACPI_6_2.pdf - 第 5.2.27 节