NUMA Memory PerformanceÂķ
NUMA LocalityÂķ
Some platforms may have multiple types of memory attached to a compute node. These disparate memory ranges may share some characteristics, such as CPU cache coherence, but may have different performance. For example, different media types and buses affect bandwidth and latency.
A system supports such heterogeneous memory by grouping each memory type under different domains, or ânodesâ, based on locality and performance characteristics. Some memory may share the same node as a CPU, and others are provided as memory only nodes. While memory only nodes do not provide CPUs, they may still be local to one or more compute nodes relative to other nodes. The following diagram shows one such example of two compute nodes with local memory and a memory only node for each of compute node:
+------------------+ +------------------+
| Compute Node 0 +-----+ Compute Node 1 |
| Local Node0 Mem | | Local Node1 Mem |
+--------+---------+ +--------+---------+
| |
+--------+---------+ +--------+---------+
| Slower Node2 Mem | | Slower Node3 Mem |
+------------------+ +--------+---------+
A âmemory initiatorâ is a node containing one or more devices such as CPUs or separate memory I/O devices that can initiate memory requests. A âmemory targetâ is a node containing one or more physical address ranges accessible from one or more memory initiators.
When multiple memory initiators exist, they may not all have the same performance when accessing a given memory target. Each initiator-target pair may be organized into different ranked access classes to represent this relationship. The highest performing initiator to a given target is considered to be one of that targetâs local initiators, and given the highest access class, 0. Any given target may have one or more local initiators, and any given initiator may have multiple local memory targets.
To aid applications matching memory targets with their initiators, the kernel provides symlinks to each other. The following example lists the relationship for the access class â0â memory initiators and targets:
# 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
A memory initiator may have multiple memory targets in the same access class. The target memoryâs initiators in a given class indicate the nodesâ access characteristics share the same performance relative to other linked initiator nodes. Each target within an initiatorâs access class, though, do not necessarily perform the same as each other.
The access class â1â is used to allow differentiation between initiators that are CPUs and hence suitable for generic task scheduling, and IO initiators such as GPUs and NICs. Unlike access class 0, only nodes containing CPUs are considered.
NUMA PerformanceÂķ
Applications may wish to consider which node they want their memory to be allocated from based on the nodeâs performance characteristics. If the system provides these attributes, the kernel exports them under the node sysfs hierarchy by appending the attributes directory under the memory nodeâs access class 0 initiators as follows:
/sys/devices/system/node/nodeY/access0/initiators/
These attributes apply only when accessed from nodes that have the are linked under the this accessâs initiators.
The performance characteristics the kernel provides for the local initiators are exported are as follows:
# 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
The bandwidth attributes are provided in MiB/second.
The latency attributes are provided in nanoseconds.
The values reported here correspond to the rated latency and bandwidth for the platform.
Access class 1 takes the same form but only includes values for CPU to memory activity.
NUMA CacheÂķ
System memory may be constructed in a hierarchy of elements with various performance characteristics in order to provide large address space of slower performing memory cached by a smaller higher performing memory. The system physical addresses memory initiators are aware of are provided by the last memory level in the hierarchy. The system meanwhile uses higher performing memory to transparently cache access to progressively slower levels.
The term âfar memoryâ is used to denote the last level memory in the hierarchy. Each increasing cache level provides higher performing initiator access, and the term ânear memoryâ represents the fastest cache provided by the system.
This numbering is different than CPU caches where the cache level (ex: L1, L2, L3) uses the CPU-side view where each increased level is lower performing. In contrast, the memory cache level is centric to the last level memory, so the higher numbered cache level corresponds to memory nearer to the CPU, and further from far memory.
The memory-side caches are not directly addressable by software. When software accesses a system address, the system will return it from the near memory cache if it is present. If it is not present, the system accesses the next level of memory until there is either a hit in that cache level, or it reaches far memory.
An application does not need to know about caching attributes in order to use the system. Software may optionally query the memory cache attributes in order to maximize the performance out of such a setup. If the system provides a way for the kernel to discover this information, for example with ACPI HMAT (Heterogeneous Memory Attribute Table), the kernel will append these attributes to the NUMA node memory target.
When the kernel first registers a memory cache with a node, the kernel will create the following directory:
/sys/devices/system/node/nodeX/memory_side_cache/
If that directory is not present, the system either does not provide a memory-side cache, or that information is not accessible to the kernel.
The attributes for each level of cache is provided under its cache level index:
/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/
Each cache levelâs directory provides its attributes. For example, the following shows a single cache level and the attributes available for software to query:
# tree /sys/devices/system/node/node0/memory_side_cache/
/sys/devices/system/node/node0/memory_side_cache/
|-- index1
| |-- indexing
| |-- line_size
| |-- size
| `-- write_policy
The âindexingâ will be 0 if it is a direct-mapped cache, and non-zero for any other indexed based, multi-way associativity.
The âline_sizeâ is the number of bytes accessed from the next cache level on a miss.
The âsizeâ is the number of bytes provided by this cache level.
The âwrite_policyâ will be 0 for write-back, and non-zero for write-through caching.
See AlsoÂķ
[1] https://www.uefi.org/sites/default/files/resources/ACPI_6_2.pdf - Section 5.2.27