Modern high-performance enterprise servers and multi-socket workstations employ Non-Uniform Memory Access (NUMA) architecture to scale memory throughput beyond the limits of a single unified bus. In a NUMA system, physical memory is partitioned across multiple local hardware nodes, each wired directly to specific CPU sockets or core complexes. While accessing memory attached locally to a processor’s socket is exceptionally fast, accessing memory wired to a remote socket introduces measurable interconnect bus latency and memory bandwidth contention.
For latency-critical enterprise workloads—including high-frequency trading engines, PostgreSQL/MySQL relational databases, in-memory caches (Redis/Memcached), and virtual machine hypervisors (KVM)—allowing the Linux kernel to assign memory and CPU cores haphazardly across NUMA boundaries degrades overall performance. The numactl command-line utility provides systems engineers with direct control over NUMA process scheduling policies, memory interleaving, and CPU node affinity.
Installing and Verifying numactl on Linux
The numactl package is available in the base repositories of all major Linux distributions:
To install on Debian and Ubuntu:
sudo apt update
sudo apt install numactl
To install on RHEL, AlmaLinux, Rocky Linux, or Fedora:
sudo dnf install numactl
To install on Arch Linux:
sudo pacman -S numactl
Inspecting Your System’s Hardware NUMA Topology
Before pinning processes or reallocating memory pools, execute numactl --hardware to view your physical server layout:
numactl --hardware
The output outputs three critical architectural indicators:
- Available Nodes: The total number of independent NUMA nodes recognized by the kernel (e.g.,
available: 2 nodes (0-1)). - Node Core Mapping: The specific logical CPU IDs wired to each node (e.g.,
node 0 cpus: 0 1 2 3 8 9 10 11). - Node Distances (Latency Matrix): The relative memory access penalty between nodes. Local access is normalized at a distance of
10. A cross-node remote access distance of21indicates that retrieving data across the socket interconnect incurs more than double the latency of local RAM.
To view the current NUMA memory allocation policy governing your active terminal shell, run:
numactl --show
Understanding NUMA Memory Allocation Policies
When launching applications via numactl, you can select from four core memory allocation policies:
--membind=<nodes>: Strictly allocates memory from the specified nodes only. If memory on the selected nodes is exhausted, allocation fails rather than spilling over to remote nodes.--preferred=<node>: Strongly prefers allocating memory from the specified node. If the preferred node runs out of available RAM, the kernel gracefully falls back to adjacent nodes without crashing the application.--interleave=<nodes>: Round-robins memory page allocations across all specified nodes evenly. This policy is ideal for large shared datasets accessed equally by threads running across all sockets, maximizing aggregate memory bandwidth.--localalloc: Directs the kernel to always allocate memory from whichever NUMA node the executing thread is currently running on.
How to Launch and Bind Processes with numactl
You can execute commands with specific CPU and memory bindings directly from the terminal:
1. Binding an Application to a Specific CPU Node and Local Memory
To launch a high-performance database daemon (such as MySQL or PostgreSQL) pinned exclusively to CPU cores and RAM on NUMA node 0:
numactl --cpunodebind=0 --membind=0 /usr/sbin/mysqld
This configuration ensures zero remote memory bus traversals, keeping cache hits high and access latency uniform.
2. Binding to Specific Logical CPU Cores
If you want to pin a process to a granular list of CPU cores (e.g., cores 0 through 3) while binding memory to node 0:
numactl --physcpubind=0-3 --membind=0 ./trading_engine_binary
3. Enabling Memory Interleaving for Large In-Memory Caches
For applications like Redis or high-throughput analytics pipelines where memory capacity requirements exceed a single socket’s RAM capacity:
numactl --interleave=all /usr/bin/redis-server /etc/redis/redis.conf
Interleaving spreads memory pages across all memory controllers simultaneously, preventing a single memory bus bottleneck.
Auditing Live NUMA Statistics and Memory Hits/Misses
To verify that your bindings are effectively preventing remote memory access penalties, monitor memory distribution using numastat:
numastat
Examine the key diagnostic metrics:
numa_hit: The number of memory allocations successfully served from the local node where the process thread was executing (higher is better).numa_miss: Allocations that intended to be local but were forced to remote memory due to low local RAM headroom.numa_foreign: Allocations intended for another node that spilled over onto this node.local_nodevs.other_node: Compares local memory hits against cross-bus remote hits. A high ratio ofother_nodetraffic indicates poor process placement requiringnumactloptimization.
To monitor a specific running process ID (PID) to confirm its current per-node memory footprint, run:
numastat -p <PID>
Leveraging numactl on Linux servers eliminates cross-socket latency bottlenecks, optimizing CPU cache locality and delivering predictable, deterministic throughput for demanding enterprise compute workloads.