核心原则是cpu与内存同numa节点以避免跨节点访问延迟翻倍;需用numactl --hardware查拓扑,依node distances量化调度;线程绑核与设置内存策略须在线程创建后立即执行;容器内存需特化allocator或重载new/delete实现本地分配;网卡/nvme设备需与处理线程及缓冲区内存同节点。

核心原则是让 CPU 和它要访问的内存尽量落在同一个 NUMA 节点上,避免跨节点访问带来的延迟翻倍和带宽瓶颈。
看清系统拓扑,先确认节点分布
运行 numactl --hardware 查清物理布局:哪些 CPU 属于哪个节点、每个节点有多少内存、节点间距离(distance)是多少。例如输出中 node 0 cpus: 0-7 表示前 8 个逻辑 CPU 全在 node 0,而 node distances: 0: 10 21 意味着 node 0 访问自身内存成本为 10,访问 node 1 内存成本为 21——延迟高出一倍以上。这个距离值比“本地/远程”标签更量化,应作为调度依据。
线程与 CPU 绑定必须早于内存分配
不能等线程启动后再绑核和设内存策略,否则全局对象构造、静态变量初始化、甚至 std::vector 的首次 push_back 都可能已在默认节点完成分配。正确做法是:
- 用 pthread_setaffinity_np 或 std::this_thread::set_affinity 在线程创建后立即绑定到目标 CPU 核心
- 紧接着调用 numa_set_preferred(node_id) 或 numa_set_localalloc(),确保后续所有 malloc/new 都落在对应节点
- 若需严格控制,改用 numa_alloc_onnode(size, node_id) 替代 malloc,并配合 numa_node_of_cpu(sched_getcpu()) 动态获取当前线程所在节点
让容器和堆内存也遵守本地性
std::vector、std::string 等默认使用全局 new,不会自动适配 NUMA。可行方案包括:
- 特化 std::allocator
,内部调用 numa_alloc_onnode 和 numa_free - 重载类级别的 operator new/delete,统一走 NUMA 分配路径
- 注意:reserve() 不触发分配,真正分配发生在第一次插入或 resize 时,所以绑定和策略设置必须在此之前完成
网络与 I/O 场景要对齐硬件亲和
网卡、NVMe 控制器通常归属某个 NUMA 节点。查 /sys/class/net/eth0/device/numa_node 得到节点号后,应把处理该设备中断和数据包的工作线程绑定到同一节点,并分配本地内存做 ring buffer、报文缓冲区等。否则即使线程绑核了,DMA 写入的内存若在远端节点,CPU 读取时仍会触发跨节点访问。










