关于 numa-programming
面向多路服务器和高性能程序开发者的 NUMA 编程指南,介绍拓扑检测、内存与线程绑定、数据结构设计及远程访问诊断。
主要功能
- 01检测 NUMA 拓扑与节点距离
- 02绑定进程、线程和内存
- 03使用 libnuma 分配与迁移内存
- 04设计按节点划分的数据结构
- 05借助 perf 诊断远程访问
- 06通过 lstopo 查看硬件布局
适用场景
- 01排查多路系统扩展性不佳
- 02让数据库或缓存使用本地内存
- 03为分片分配器设计节点内存池
- 04比较本地与远程内存访问
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill numa-programming安装后在 Claude Code 中输入/numa-programming确认已加载。
详细介绍
NUMA 编程
用途
引导 Agent 进行 NUMA 感知编程:使用 numactl 和 sysfs 检测拓扑、使用 libnuma API(numa_alloc_onnode、mbind、set_mempolicy)、使用 numactl 绑定进程、设计 NUMA 感知数据结构、使用 perf stat 诊断远程访问,以及使用 lstopo 可视化。
适用场景
- 多路服务器的 CPU 利用率较低,但扩展性不佳
- 某个 NUMA 节点的内存带宽饱和
- 将数据库或缓存进程绑定到本地内存
- 设计按节点划分的空闲链表或分片分配器
- 测量远程与本地内存访问延迟
- 为 socket 局部性调优 HPC、DPDK 或自定义分配器
工作流
1. 检测拓扑
# Hardware topology summary
numactl --hardware
# Detailed topology with distances
lstopo --of console
# sysfs nodes
ls /sys/devices/system/node/
cat /sys/devices/system/node/node0/meminfo
cat /sys/devices/system/node/node0/cpulist
典型输出:
available: 2 nodes (0-1)
node 0 cpus: 0-15
node 0 size: 65536 MB
node 1 cpus: 16-31
node 1 size: 65536 MB
node distances:
node 0 1
0: 10 21
1: 21 10
距离 10 表示本地,更高的值表示远程(跨 socket)。
2. 使用 numactl 绑定进程
# Bind to node 0 CPUs and memory
numactl --cpunodebind=0 --membind=0 ./myapp
# Interleave memory across all nodes
numactl --interleave=all ./myapp
# Preferred node (fallback if full)
numactl --preferred=0 ./myapp
# Show process NUMA policy
numactl --show
cat /proc/self/numa_maps
3. libnuma API
#include <numa.h>
#include <numaif.h>
#include <stdio.h>
int main(void) {
if (numa_available() < 0) {
fprintf(stderr, "NUMA not available\n");
return 1;
}
int node = numa_node_of_cpu(0);
printf("CPU 0 on node %d\n", node);
// Allocate on specific node
size_t size = 1024 * 1024 * 1024;
void *mem = numa_alloc_onnode(size, 0);
if (!mem) return 1;
// Bind existing memory
unsigned long nodemask = 1UL << 0;
mbind(mem, size, MPOL_BIND, &nodemask, sizeof(nodemask) * 8, 0);
numa_free(mem, size);
return 0;
}
gcc -o numa_test numa_test.c -lnuma
| API | 用途 |
|---|---|
numa_alloc_onnode | 在指定节点上分配 |
numa_alloc_local | 在当前 CPU 所属节点上分配 |
mbind | 为现有映射设置策略 |
set_mempolicy | 设置后续分配的默认策略 |
move_pages | 将页面迁移到目标节点 |
4. NUMA 感知数据结构
// Per-node freelist — avoids cross-node synchronization
#define MAX_NODES 8
struct per_node_pool {
void *free_list[MAX_NODES];
int node_count;
};
void *pool_alloc_numa(struct per_node_pool *p) {
int node = numa_node_of_cpu(sched_getcpu());
void *blk = p->free_list[node];
if (blk) {
p->free_list[node] = *(void **)blk;
return blk;
}
return numa_alloc_onnode(BLOCK_SIZE, node);
}
将线程固定到与其内存池位于同一节点上的核心。
5. 对齐线程亲和性
# Pin thread 0 to CPU 0 (node 0), allocate on node 0
numactl --cpunodebind=0 --membind=0 ./worker --id 0
numactl --cpunodebind=1 --membind=1 ./worker --id 1
#include <pthread.h>
#include <sched.h>
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(target_cpu, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
6. 诊断远程访问
# Cache misses often spike with remote memory
perf stat -e cache-misses,cache-references,node-load-misses \
numactl --cpunodebind=0 --membind=1 ./myapp
# Compare local vs remote binding
perf stat numactl --cpunodebind=0 --membind=0 ./myapp
perf stat numactl --cpunodebind=0 --membind=1 ./myapp
# NUMA hit/miss stats (if available)
perf stat -e node-loads,node-load-misses,node-stores ./myapp
7. 测量远程访问开销
// Microbenchmark: touch 1GB on local vs remote node
clock_t start = clock();
for (size_t i = 0; i < size; i += 4096)
sum += ((char *)mem)[i];
根据互连(QPI/UPI/Infinity Fabric)的不同,远程访问预计会慢 1.5–3 倍。
8. 使用 lstopo 可视化
# Graphical (if X11)
lstopo
# Text with memory/PCI
lstopo --of ascii
# Export for documentation
lstopo file.png
显示:NUMA 节点、核心、缓存、PCI 设备——对于 DPDK 网卡部署至关重要。
9. 决策树
Poor scaling on multi-socket?
├── Check numactl --hardware
├── Verify thread and memory on same node
├── perf stat node-load-misses
├── Remote misses high?
│ ├── numactl --membind=local
│ └── Per-node data partitioning
└── Still slow → memory bandwidth bound; reduce sharing
常见问题
| 症状 | 原因 | 解决方法 |
|---|---|---|
| 某个节点内存耗尽,但其他节点仍有空闲内存 | MPOL_BIND 过于严格 | 使用 --preferred 或交错分配 |
| 线程数增加后速度慢了 2 倍 | 远程内存访问 | 使用 numactl --membind,与 CPU 节点匹配 |
| 基准测试结果不一致 | 操作系统迁移了页面 | 使用 mbind 设置 MPOL_BIND;必要时使用 mlock |
| DPDK 网卡位于错误的 socket | PCI 设备离 CPU 较远 | 使用 lstopo;将 EAL 绑定到本地 socket |
| 找不到 libnuma | 未安装软件包 | apt install libnuma-dev |
| 首次触碰策略令人意外 | 在节点 0 上分配,却在节点 1 上运行 | 从已绑定的线程进行分配 |
相关技能
skills/allocators/custom-allocators— 按节点划分的内存池分配器skills/async-io/dpdk—rte_malloc_socket、网卡 NUMA 局部性skills/hpc/mpi— 按 NUMA 节点绑定 MPI 进程skills/profilers/hardware-counters— 测量缓存未命中skills/profilers/linux-perf— perf NUMA 事件skills/low-level-programming/cpu-cache-opt— 缓存局部性基础
评论
支持 Markdown