NUMA 内存性能¶
NUMA 局部性¶
某些平台可能连接了多种类型的内存到计算节点。这些不同类型的内存可能共享一些特性,例如 CPU 缓存一致性,但性能可能不同。例如,不同的介质类型和总线会影响带宽和延迟。
系统通过将每种内存类型根据局部性和性能特性分组到不同的域或“节点”下,来支持这种异构内存。某些内存可能与 CPU 共享同一节点,而其他内存则作为纯内存节点提供。虽然纯内存节点不提供 CPU,但它们相对于其他节点,可能仍然与一个或多个计算节点局部。下图展示了两个计算节点及其本地内存以及每个计算节点的纯内存节点的一个示例。
+------------------+ +------------------+
| Compute Node 0 +-----+ Compute Node 1 |
| Local Node0 Mem | | Local Node1 Mem |
+--------+---------+ +--------+---------+
| |
+--------+---------+ +--------+---------+
| Slower Node2 Mem | | Slower Node3 Mem |
+------------------+ +--------+---------+
“内存发起方”(memory initiator)是包含一个或多个设备(如 CPU 或独立的内存 I/O 设备)的节点,这些设备可以发起内存请求。“内存目标”(memory target)是包含一个或多个物理地址范围的节点,这些地址范围可由一个或多个内存发起方访问。
当存在多个内存发起方时,它们在访问给定内存目标时可能不会都具有相同的性能。每个发起方-目标对可以被组织成不同的访问类等级,以表示这种关系。访问给定目标时性能最高的发起方被认为是该目标的本地发起方之一,并被赋予最高的访问类,即 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 因而适用于通用任务调度的发起方,以及 GPU 和网卡等 I/O 发起方。与访问类 0 不同,只考虑包含 CPU 的节点。
NUMA 性能¶
应用程序可能希望根据节点的性能特性来决定从哪个节点分配内存。如果系统提供这些属性,内核会将它们导出到节点 sysfs 层级结构下,方法是在内存节点的访问类 0 发起方下附加属性目录,如下所示:
/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 缓存¶
系统内存可以构建成一个具有各种性能特性的分层结构,以便通过较小、性能较高的内存缓存来提供较大、性能较低的内存的地址空间。内存发起方所感知的系统物理地址由层次结构中的最后一级内存提供。同时,系统使用性能较高的内存透明地缓存对逐渐较慢级别的访问。
“远端内存”(far memory)一词用于表示层次结构中的最后一级内存。每个增加的缓存级别都提供更高性能的发起方访问,“近端内存”(near memory)一词代表系统提供的最快缓存。
这种编号与 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
如果缓存是直接映射缓存,则“indexing”为 0;对于任何其他基于索引的多路关联性,则为非零值。
“line_size”是指在未命中时从下一缓存级别访问的字节数。
“size”是指此缓存级别提供的字节数。
“write_policy”对于写回(write-back)缓存为 0,对于写穿(write-through)缓存为非零。
另请参阅¶
[1] https://www.uefi.org/sites/default/files/resources/ACPI_6_2.pdf - 第 5.2.27 节