当前位置: 首页 > news >正文

性能调优之NUMA调优

什么是NUMA

NUMA(Non-Uniform Memory Access,非统一内存访问)是一种用于多处理器系统的内存设计架构。在NUMA系统中,每个处理器(或一组处理器)拥有自己的本地内存,处理器访问本地内存的速度比访问其他处理器的内存(远端内存)要快。

image

那你可能会有一个疑问,服务器会在什么时候出现跨NUMA的情况呢?

第一种场景:本地节点内存不足,或内存分配策略未绑定节点,导致CPU不得不访问其他节点的内存。

1、当某一节点的 CPU 进程占用内存超过其本地内存上限时,服务器会自动将超出部分的内存分配到其他 NUMA 节点的 “远程内存” 中,此时该 CPU 访问这部分内存就会触发跨 NUMA。

# 服务器有2个NUMA节点(节点0:CPU 0-7 + 128GB 本地内存;节点1:CPU 8-15 + 128GB 本地内存)。仅在节点0的CPU 0上运行一个需要150GB内存的数据库进程:节点0的128GB本地内存耗尽后,剩余22GB内存会被分配到节点1的本地内存中。此时CPU 0访问这22GB内存时,必须跨节点通过NUMA互联总线(如Intel UPI、AMD Infinity Fabric)访问节点1的内存,形成跨NUMA。

2、Linux/Unix系统默认的内存分配策略(如default策略)是 “优先分配本地内存,但本地不足时分配远程内存”;但如果未配置“强绑定策略”(如membind),即使本地内存充足,也可能因内核调度逻辑导致内存被分配到其他节点。

# 运维未通过工具(如numactl、taskset)绑定进程的内存节点,仅绑定了CPU核心。例如:将进程绑定到节点0的CPU 0,但内存分配未限制为节点0,内核可能因 “内存碎片优化” 将部分内存分配到节点1,导致跨NUMA。
# 虚拟化场景中,虚拟机(VM)的内存未与物理NUMA节点对齐:若VM的vCPU绑定到节点0,但VM的内存被KVM分配到节点1的物理内存,vCPU访问内存时会跨NUMA。

3、当多个NUMA节点的CPU需要访问同一块共享内存(如进程间通信的shm、分布式缓存的共享段)时,若共享内存被分配到某一节点(如节点 0),则其他节点(如节点1)的CPU访问该共享内存时,必然触发跨NUMA。

# 节点0的CPU 0创建一块10GB的共享内存,用于与节点1的CPU 8通信;节点1的CPU 8读取 / 写入该共享内存时,需跨节点访问节点0的本地内存,形成跨NUMA。

第二种场景:CPU访问非本地节点的PCIe设备

1、设备与 CPU 未绑定到同一 NUMA 节点

# 网卡场景:若网卡直连节点1的 PCIe 总线(归属节点1),但处理网卡流量的进程(如Nginx、DPDK 应用)被绑定到节点0的CPU,此时节点0的CPU需要跨节点访问节点1的网卡,导致数据传输延迟升高。
# SSD 场景:若 NVMe SSD归属节点0,但运行数据库的进程被绑定到节点1的CPU,CPU读取SSD数据时需跨NUMA,影响IO性能。

2. 多设备负载集中在单一节点,其他节点需跨节点调用

# 例如:服务器有2个NUMA节点,仅节点0连接了2块高速SSD,节点1无本地SSD。当节点1的CPU需要读取数据时,必须跨节点访问节点0的SSD,触发跨NUMA。

第三种场景:CPU核心与进程/线程的节点不匹配

1、进程未绑定 NUMA 节点,被调度到其他节点的 CPU

# 示例:某进程的内存分配在节点0,但其被调度器分配到节点1的CPU 8上运行。此时CPU 8访问该进程的内存(节点 0),就会触发跨 NUMA。
# 常见于默认调度策略(如Linux的CFS调度器):调度器优先考虑CPU负载均衡,而非NUMA节点亲和性,可能导致 “进程-内存-CPU” 跨节点。

2. 虚拟化环境中 vCPU 与物理 NUMA 节点错位

# 若虚拟机的vCPU配置未与物理NUMA节点 “对齐”(如vCPU跨越物理节点0和1),KVM等虚拟化层可能将vCPU调度到不同物理节点的CPU上,导致vCPU访问VM内存时跨物理NUMA节点。
# 例如:VM配置4个vCPU,被分配到物理节点0的 CPU 0-2和节点1的CPU 8,此时vCPU 3(对应物理CPU8)访问VM内存(若分配到节点0)时,会跨NUMA。

第四章场景:NUMA节点配置错误或硬件限制

1. BIOS / 固件配置错误,导致 NUMA 节点识别异常

# 若服务器BIOS中未启用 NUMA(或配置为 “UMA 模式”,即均匀内存访问),但实际硬件支持NUMA,可能导致系统误将不同节点的资源视为同一节点,或反之将同一节点拆分为多个,间接引发跨NUMA。# 例如:BIOS中 “NUMA Node Interleaving”(NUMA节点交错)被启用,会强制内存跨节点均匀分配,导致所有CPU访问内存时都可能跨NUMA(除非进程强绑定)。

2. 硬件资源不足,不得不跨节点使用

# 例如:服务器某一NUMA节点的CPU核心全部被占用(如节点0的8个CPU满负载),但仍有新进程需要运行,调度器只能将新进程分配到节点1的CPU,若新进程的内存/设备在节点0,则必然跨NUMA。

numactl工具

numactl工具可用于查看当前服务器的NUMA节点配置、状态,可通过该工具将进程 绑定到指定CPU core,由指定CPU core来运行对应进程。如果系统中没有这个命令可以通过yum -y install numactl numastat来进行安装

使用方法如下

numactl [ --interleave nodes ] [ --preferred node ] [ --membind nodes ] [ --cpunodebind nodes ] [ --physcpubind cpus ] [ --localalloc ] command {arguments ...}
# 常用参数
# --hardware 显示系统上可用节点的清单,包括节点之间的相对距离。
# -N --cpunodebind 确保指定的命令及其子进程仅在指定节点上执行。
# -l --localalloc 指定始终从本地节点分配内存。
# -i --interleave=0,1|all:设置内存交错策略,内存将在指定的节点之间循环分配。
# --preferred=node:设置首选节点,如果可能,内存将被分配到这个节点上。
# -m --membind=nodes:设置内存绑定策略,只从指定的节点分配内存。
# --cpunodebind=nodes:设置CPU节点绑定,只在指定节点的CPU上执行命令。
# -C --physcpubind=cpus:设置物理CPU绑定,只在指定的CPU上执行进程。

步骤一、通过查看当前服务器的NUMA配置。

1、检查NUMA节点的内存访问统计

[root@localhost ~]# numastat 
                           node0           node1           node2           node3
numa_hit                     414          552466             414           55540
numa_miss                      0               0               0               0
numa_foreign                   0               0               0               0
interleave_hit                 0            3005               0            2847
local_node                     0          224468               0           15870
other_node                   414          327998             414           39670# numa_hit:该节点成功分配本地内存访问的内存大小
# numa_miss:内存访问分配到另一个node的大小,该值和另一个node的numa_foreign相对应
# numa_foreign 其他节点分配失败,由本节点代为分配的次数
# interleave_hit 通过交错策略在本节点分配的次数
# local_node:该节点的进程成功在本节点上分配内存访问的大小
# other_node:该节点进程在其它节点上分配的内存访问的大小# 或者通过下面命令
[root@localhost ~]# cat /sys/devices/system/node/node*/numastat
numa_hit 210079
numa_miss 0
numa_foreign 0
interleave_hit 1412 
local_node 206615
other_node 3464 
...

2、确认硬件拓扑和NUMA架构

[root@localhost ~]# lscpu | grep -i numa
NUMA 节点:                         4
NUMA 节点0 CPU:                    0-23
NUMA 节点1 CPU:                    24-47
NUMA 节点2 CPU:                    48-71
NUMA 节点3 CPU:                    72-95

3、这告诉你系统有4个NUMA节点,以及每个节点包含了哪些CPU核心。

[root@localhost ~]# numactl -H
available: 4 nodes (0-3)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
node 0 size: 130333 MB
node 0 free: 129064 MB
node 1 cpus: 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
node 1 size: 130928 MB
node 1 free: 130552 MB
node 2 cpus: 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
node 2 size: 130898 MB
node 2 free: 130319 MB
node 3 cpus: 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
node 3 size: 129903 MB
node 3 free: 128242 MB
node distances:
node   0   1   2   3 0:  10  12  20  22 1:  12  10  22  24 2:  20  22  10  12 3:  22  24  12  10 

关键信息:

  • node distances:节点间的访问代价。node0访问node0的内存代价是10,访问node1的内存代价是12(越高越慢)。这清晰地显示了跨节点访问的性能 penalty。

步骤二、通过numactl将进程绑定到指定CPU core。

1、通过 numactl -C 5-15 dd命令即是将进程“dd”绑定到5~15 CPU core上执行

[root@localhost ~]# numactl -C 5-15 --membind=0 dd if=/dev/zero of=/dev/shm/A bs=1M count=1024
记录了1024+0 的读入
记录了1024+0 的写出
1073741824字节(1.1 GB,1.0 GiB)已复制,0.227962 s,4.7 GB/s
[root@localhost ~]# numactl -C 5-15 --membind=1 dd if=/dev/zero of=/dev/shm/A bs=1M count=1024
记录了1024+0 的读入
记录了1024+0 的写出
1073741824字节(1.1 GB,1.0 GiB)已复制,0.227861 s,4.7 GB/s
[root@localhost ~]# numactl -C 5-15 --membind=2 dd if=/dev/zero of=/dev/shm/A bs=1M count=1024
记录了1024+0 的读入
记录了1024+0 的写出
1073741824字节(1.1 GB,1.0 GiB)已复制,0.332766 s,3.2 GB/s
[root@localhost ~]# numactl -C 5-15 --membind=3 dd if=/dev/zero of=/dev/shm/A bs=1M count=1024
记录了1024+0 的读入
记录了1024+0 的写出
1073741824字节(1.1 GB,1.0 GiB)已复制,0.348661 s,3.1 GB/s
http://www.hskmm.com/?act=detail&tid=11040

相关文章:

  • 深入解析:SpringMVC静态资源与Servlet容器指南
  • CCPC Online 2025 游寄
  • CentOS 7 容器时遇到了 yum update 报错
  • MIT新论文:数据即上限,扩散模型的关键能力来自图像统计规律,而非复杂架构
  • 基于MATLAB的视频动态目标跟踪检测搭建方案
  • U522155 数据生成(小心电脑)
  • 实用指南:OSG中osgFX库
  • 如何将带有线网卡和无线网卡的台式机作为网关/路由器
  • 2025.9.20——1橙
  • 日期
  • 【GAN网络解惑】面向产品的优化:推理裁剪、蒸馏、INT8/FP8 量化,GAN 的真实延迟如何打下来? - 教程
  • 资本与资本主义
  • 202509_NBWS_encoded_csv
  • 滑雪
  • 守序者的尊严
  • 在Ubuntu22.04平台上交叉编译针对Rv1126架构的GCC13.2.0编译器
  • 深度学习(DBBNet重参数化)
  • CAR 细胞疗法:肝癌治疗的曙光与荆棘
  • Java项目案例作业1
  • 配置Spring框架以连接SQL Server数据库
  • 这一辈子大多数日子是无聊的
  • Go 实现验证码识别
  • 跳出 AI 编程的「兔子洞」,4 个实战策略帮你解决90%的死循环
  • 用 PHP 和 Tesseract OCR 识别英文数字验证码
  • 凝望深渊时,深渊也凝望着你(黑洞与摇钱树)
  • 详细介绍:《Vuejs设计与实现》第 16 章(解析器) 中
  • spring项目部署后为什么会生成 logback-spring.xml记录
  • 【解决】Matlab函数体突然不自动缩进了
  • 202509_NBWS_logbool
  • Kubernetes权威指南-深入理解Pod Service