c++ numa编程 c++如何优化跨numa节点的内存访问

陌枫酱_2535

陌枫酱_2535

2026-03-22

299人浏览

原创

跨numa节点访问内存变慢因远端延迟高、带宽低,并易触发缓存一致性风暴和内存控制器争用;需通过numa_alloc_onnode、mpol_preferred等策略实现线程与内存同节点绑定。

c++ numa编程 c++如何优化跨numa节点的内存访问

为什么跨 NUMA 节点访问内存会变慢

因为 CPU 访问本地节点的内存延迟低(约 100ns),而访问远端节点要走 QPI/UPI 链路,延迟翻倍甚至更高,带宽也受限。这不是“慢一点”,而是可能触发缓存一致性协议风暴、加剧内存控制器争用。

常见错误现象:perf stat 显示 LLC-misses 高、remote-dram 计数飙升;numastat 显示进程在 Node 0 分配但大量在 Node 1 上缺页。

  • 默认 malloc 或 new 不感知 NUMA,分配位置由内核当前策略决定(通常是 current node)
  • 线程绑定到 Node 1 的 CPU,但内存仍在 Node 0 分配 → 必然跨节点访问
  • mmap(MAP_ANONYMOUS) 默认也不保 locality,除非显式指定 MPOL_BIND 等策略

如何让 new/malloc 分配本地内存

核心是控制内存分配策略(policy)和绑定(binding),不是靠改代码逻辑,而是靠运行时干预或显式调用 NUMA API。

最直接的方式:用 libnuma 的 numa_alloc_onnode 替代 malloc:

void* ptr = numa_alloc_onnode(size, node_id); // 绑定到指定 node

但注意:C++ operator new 不自动调用它,所以得重载或封装。

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载
  • 若用 std::vector,别指望 reserve() 自动本地化;需配合 std::allocator 特化,底层调用 numa_alloc_onnode
  • 全局设置策略(如 numactl --membind=1 ./a.out)对 malloc 有效,但对已加载的共享库或 mmap 区域不一定生效
  • numa_set_localalloc() 只影响后续分配,且仅对当前线程有效;多线程必须每个线程都调用

线程与内存节点如何对齐才不踩坑

错位是跨 NUMA 性能问题的主因:线程在 Node 0 CPU 运行,却频繁访问 Node 1 的内存。对齐不是“尽量靠近”,而是“严格绑定+分配同节点”。

典型错误场景:OpenMP 启动 8 个线程,系统自动调度到混合节点的 CPU 上,但所有堆内存都在启动时的 Node 0 分配。

  • 用 pthread_setaffinity_np + numa_node_of_cpu 获取线程所在 node,再调用 numa_set_preferred 或 numa_alloc_onnode
  • 避免混用:不要一边用 numactl --cpunodebind=0 --membind=0,另一边又在代码里调用 numa_set_interleave_mask,策略会冲突
  • std::thread 构造后立即绑定 CPU 和设置内存策略,否则线程可能已在其他 node 上执行了部分初始化代码(包括 global object 构造函数里的 new)

什么时候该用 MPOL_PREFERRED 而不是 MPOL_BIND

MPOL_BIND 强制只从指定节点分配,一旦该节点内存耗尽就 ENOMEM;MPOL_PREFERRED 是 fallback 更友好的策略——优先本地,不足时才跨节点,适合内存用量不确定的服务。

用 mbind 设置已有内存区域策略时,注意范围必须是 page-aligned 且长度是 page size 整数倍:

mbind(ptr, size, MPOL_PREFERRED, &nodemask, maxnode, 0);
  • MPOL_BIND 适合固定大小的高性能 buffer(如 DPDK ring、自定义 arena),可控性强
  • MPOL_INTERLEAVE 对读密集型随机访问有帮助(分散压力),但写密集时反而增加 coherency traffic,慎用
  • 对 mmap 区域设策略,必须在 mmap 后立即调用 mbind,且该区域不能已发生缺页(否则策略不生效)

真正难的是动态 workload:内存增长不可控、线程数随负载变化、容器环境节点拓扑不透明。这时候硬编码 node id 或依赖 numactl 参数就容易失效,得结合 numa_node_size64 运行时探测可用性。

C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!

相关文章

c++速学教程(入门到精通)
c++速学教程(入门到精通)

c++怎么学习?c++怎么入门?c++在哪学?c++怎么学才快?不用担心,这里为大家提供了c++速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

c++

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

2023.07.18

5607

5

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2023.08.10

2428

6

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

2023.08.10

4078

6

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

2025.12.24

1129

20

java多线程相关教程合集
java多线程相关教程合集

本专题整合了java多线程相关教程,阅读专题下面的文章了解更多详细内容。

2026.01.21

383

30

C++多线程相关合集
C++多线程相关合集

本专题整合了C++多线程相关教程,阅读专题下面的的文章了解更多详细内容。

2026.01.21

559

24

C# 多线程与异步编程
C# 多线程与异步编程

本专题深入讲解 C# 中多线程与异步编程的核心概念与实战技巧,包括线程池管理、Task 类的使用、async/await 异步编程模式、并发控制与线程同步、死锁与竞态条件的解决方案。通过实际项目,帮助开发者掌握 如何在 C# 中构建高并发、低延迟的异步系统,提升应用性能和响应速度。

2026.02.06

389

20

C++多线程并发控制与线程安全设计实践
C++多线程并发控制与线程安全设计实践

本专题围绕 C++ 在高性能系统开发中的并发控制技术展开,系统讲解多线程编程模型与线程安全设计方法。内容包括互斥锁、读写锁、条件变量、原子操作以及线程池实现机制,同时结合实际案例分析并发竞争、死锁避免与性能优化策略。通过实践讲解,帮助开发者掌握构建稳定高效并发系统的关键技术。

2026.03.16

213

22

Java 多线程与并发编程实战
Java 多线程与并发编程实战

深入讲解 Java 并发编程体系,涵盖 Thread / Runnable / Callable 线程创建方式、线程生命周期与状态转换、synchronized 同步锁与 Lock/ReentrantLock 显式锁、volatile 可见性保证、ThreadPool 线程池配置与调优(核心参数/拒绝策略)、JUC 并发工具类(CountDownLatch/CyclicBarrier/Semaphore/ConcurrentHashMa

2026.04.17

349

31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习