Table of Contents

numactl OpenMP

Combining numactl with OpenMP ensures threads land on intended cores with local memory. OpenMP's affinity mechanism (OMP_PLACES, OMP_PROC_BIND) specifies which threads run on which cores; numactl enforces it system-wide and controls memory allocation.

Basic setup:

export OMP_NUM_THREADS=8
export OMP_PLACES="{0:4},{4:4}"       # threads 0-3 on core 0, threads 4-7 on core 4
export OMP_PROC_BIND=close
numactl --cpunodebind=0 --membind=0 ./app

OMP_PLACES specifies thread placement (cores 0-3 and 4-7). OMP_PROC_BIND=close keeps threads close to their initial placement. numactl enforces that the entire process stays on node 0.

More complex affinity:

export OMP_PLACES="{0,1,2,3},{4,5,6,7}"  # explicit core lists
export OMP_PROC_BIND=spread              # spread threads across places

OMP_PLACES can list cores explicitly (comma-separated) or use ranges. OMP_PROC_BIND=spread distributes threads evenly across places; OMP_PROC_BIND=close clusters them.

Per-NUMA-node binding:

export OMP_NUM_THREADS=8
export OMP_PLACES="{0:4},{4:4}"
export OMP_PROC_BIND=close
numactl --cpunodebind=0-1 --membind=0-1 --interleave=0-1 ./app

Threads 0-3 run on cores 0-3 (node 0), threads 4-7 on cores 4-7 (node 1). Memory is interleaved across both nodes. Each thread's private data (stack, thread-local storage) lives on its home node.

Nested OpenMP with NUMA:

export OMP_NUM_THREADS=4
export OMP_PLACES="{0,1,2,3}"
export OMP_PROC_BIND=close
numactl --cpunodebind=0 --membind=0 ./app

Bind the entire process to node 0, then let OpenMP do fine-grained affinity within that node.

Debugging affinity:

export OMP_DISPLAY_ENV=true
numactl --cpunodebind=0 --membind=0 ./app

OMP_DISPLAY_ENV=true prints OpenMP settings at startup, showing which cores threads are pinned to.

Performance tip: Ensure OMP_NUM_THREADS matches the cores you're pinning to. Pinning to 4 cores but setting OMP_NUM_THREADS=8 causes contention and defeats the purpose.

Note: OpenMP affinity is a hint; numactl is a guarantee. numactl overrides OS scheduling, ensuring the process stays pinned even if OpenMP's affinity isn't perfectly configured.