mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
489 字
1 分钟
为什么 NUMA 会影响程序的延迟
2023-10-20

在现代服务器上,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 --show

4.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-server

5.3 MySQL 的 NUMA 优化#

# innodb_buffer_pool_size 设置
# 应该设为节点本地内存大小,而不是总内存
# 查看内存
numactl --hardware | grep "node 0 size"

六、总结#

6.1 NUMA 影响程序延迟的原因#

原因说明
远程访问延迟跨节点访问需要互联网络
带宽限制跨节点带宽低于本地带宽
缓存一致性跨节点缓存同步开销

6.2 NUMA 优化策略#

策略方法
本地分配在 CPU 所在节点分配内存
线程亲和性将线程绑定到特定 CPU
数据局部性保持相关数据在同一节点
交叉分配对性能不敏感的数据使用交织分配

核心观点:在 NUMA 系统上,程序的内存分配和线程调度策略会显著影响性能。NUMA 感知是现代服务器端开发的重要考量。

参考资料#

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

为什么 NUMA 会影响程序的延迟
https://blog.souloss.com/posts/why-the-design/why-numa-affects-program-latency/
作者
Souloss
发布于
2023-10-20
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时