489 字
1 分钟
为什么 NUMA 会影响程序的延迟
在现代服务器上,NUMA(Non-Uniform Memory Access)架构是影响性能的重要因素。为什么 NUMA 会影响程序延迟?深入了解。
一、NUMA 之前:SMP 架构
1.1 SMP(对称多处理器)
flowchart LR
subgraph SMP 架构
CPU1[CPU 1]
CPU2[CPU 2]
CPU3[CPU 3]
CPU4[CPU 4]
B[共享总线]
M[共享内存]
end
CPU1 --> B
CPU2 --> B
CPU3 --> B
CPU4 --> B
B --> M
Note: 所有 CPU 通过同一总线访问内存<br/>总线带宽成为瓶颈
SMP 的问题:
- 所有 CPU 共享同一条内存总线
- CPU 数量增加时,总线带宽成为瓶颈
- 内存访问延迟随 CPU 数量增加而增加
二、NUMA 架构
2.1 NUMA 的基本结构
flowchart LR
subgraph Node 0
CPU1[CPU 1]
CPU2[CPU 2]
M1[本地内存<br/>64GB]
end
subgraph Node 1
CPU3[CPU 3]
CPU4[CPU 4]
M2[本地内存<br/>64GB]
end
CPU1 --> M1
CPU2 --> M1
CPU3 --> M2
CPU4 --> M2
CPU1 -.->|跨节点访问| M2
CPU3 -.->|跨节点访问| M1
I[互联网络]
M1 <--> I
M2 <--> I
2.2 NUMA 的优势
| 优势 | 说明 |
|---|---|
| 可扩展性 | 增加节点不降低性能 |
| 本地访问快 | CPU 访问本地内存延迟低 |
| 带宽扩展 | 每个节点有独立内存带宽 |
2.3 NUMA 的代价
| 访问类型 | 延迟 | 带宽 |
|---|---|---|
| 本地内存访问 | ~100 ns | 高 |
| 跨节点内存访问 | ~200-300 ns | 低 |
三、NUMA 与程序性能
3.1 本地访问 vs 远程访问
// 测试 NUMA 效应// 线程在 Node 0 运行
char *local = numa_alloc_onnode(size, 0); // Node 0 内存char *remote = numa_alloc_onnode(size, 1); // Node 1 内存
// 本地访问start = rdtsc();for (i = 0; i < N; i++) sum += local[i];local_latency = rdtsc() - start;
// 远程访问start = rdtsc();for (i = 0; i < N; i++) sum += remote[i];remote_latency = rdtsc() - start;
printf("本地: %lu 周期\n", local_latency);printf("远程: %lu 周期 (慢 %.1f 倍)\n", remote_latency, (float)remote_latency / local_latency);3.2 性能影响
flowchart LR
subgraph NUMA 效应
A[本地内存访问] -->|基准| B[100% 性能]
C[远程内存访问] -->|延迟增加| D[70-90% 性能]
end
四、NUMA 感知编程
4.1 查看 NUMA 拓扑
# 查看 NUMA 拓扑numactl --hardware# available: 2 nodes (0-1)# node 0 size: 65536 MB# node 1 size: 65536 MB# node distances:# node 0 1# 0: 10 21# 1: 21 10
# 查看当前策略numactl --show4.2 设置 NUMA 策略
# 在指定节点运行程序numactl --cpunodebind=0 --membind=0 ./my_program
# 使用交织策略(轮询分配)numactl --interleave=all ./my_program
# 查看进程的 NUMA 映射numastat -p $(pidof my_program)4.3 程序员的 NUMA 感知
// 使用 libnuma 进行 NUMA 感知编程#include <numa.h>
void process_data() { struct bitmask *cpus = numa_allocate_cpumask(); numa_parse_cpumask("0-3", cpus); // 使用 CPU 0-3
// 在 CPU 0-3 所在的节点分配内存 void *data = numa_alloc_onnode( size, numa_node_of_cpu(numa_bitmask_first(cpus)) );
// 在这些 CPU 上运行 numa_run_on_cpumask(cpus); numa_bind();
// 处理数据...
numa_free(data, size);}五、数据库的 NUMA 优化
5.1 PostgreSQL 的 NUMA 感知
# PostgreSQL 配置文件# postgresql.conf
# 共享内存使用shared_memory_type = mmap # 减少跨 NUMA 节点内存访问
# 内存设置effective_cache_size = 24GB # 设为总内存的 75%5.2 Redis 的 NUMA 感知
# 启动 Redis 时绑定到特定节点numactl --cpunodebind=0 --membind=0 redis-server5.3 MySQL 的 NUMA 优化
# innodb_buffer_pool_size 设置# 应该设为节点本地内存大小,而不是总内存
# 查看内存numactl --hardware | grep "node 0 size"六、总结
6.1 NUMA 影响程序延迟的原因
| 原因 | 说明 |
|---|---|
| 远程访问延迟 | 跨节点访问需要互联网络 |
| 带宽限制 | 跨节点带宽低于本地带宽 |
| 缓存一致性 | 跨节点缓存同步开销 |
6.2 NUMA 优化策略
| 策略 | 方法 |
|---|---|
| 本地分配 | 在 CPU 所在节点分配内存 |
| 线程亲和性 | 将线程绑定到特定 CPU |
| 数据局部性 | 保持相关数据在同一节点 |
| 交叉分配 | 对性能不敏感的数据使用交织分配 |
核心观点:在 NUMA 系统上,程序的内存分配和线程调度策略会显著影响性能。NUMA 感知是现代服务器端开发的重要考量。
参考资料
- NUMA Performance — NUMA 性能分析
- libnuma Documentation — NUMA API
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时
相关文章 智能推荐
1
CPU 全景:为什么后端工程师需要理解 CPU
CPU与计算机体系结构 从后端工程师的视角俯瞰现代 CPU——内存墙、摩尔定律的终结、多核趋势、微架构概览,以及为什么理解 CPU 体系结构是性能优化的必修课。
2
为什么 TCP 协议有性能问题
技术科普 深入解析 TCP 协议的性能瓶颈——队头阻塞、连接创建开销、拥塞控制等设计考量。
3
为什么 React 使用虚拟 DOM
技术科普 深入解析 React 虚拟 DOM 的设计原理,理解声明式 UI 与命令式 DOM 操作的本质区别。
4
为什么 CSS 选择器从右向左解析
技术科普 深入解析 CSS 选择器从右向左解析的性能优化原理,理解浏览器渲染引擎的设计考量。
5
为什么 Redis 选择单线程模型
技术科普 深入解析 Redis 选择单线程模型的设计哲学,为什么单线程能够实现高性能,以及多线程版本的 Redis 如何演进。






