
我在负责优化香港某台高性能双路物理服务器的多线程任务调度时,曾经遇到一个极其隐蔽但致命的问题——CPU利用率始终达不到预期,系统负载看似合理,但业务延迟时高时低,典型的“抖动”行为让整个系统毫无规律地掉速。最后定位的根因,正是NUMA架构下线程与内存未正确绑定,导致大量跨NUMA节点访问,触发了昂贵的远程内存访问延迟。
这篇文章将分享我在香港部署环境下,如何基于Linux NUMA架构在双路物理机中正确绑定线程与内存,避免性能抖动的实操经验和落地方案。
一、背景与环境说明
物理机配置
- 位置:香港新界BGP双线路数据中心
- 服务器型号:Dell R7525 双路AMD EPYC 7453
- 核心数:每路48核,共96核(192线程)
- 内存配置:每路512GB DDR4,共计1TB
- 系统版本:Rocky Linux 8.9(兼容RHEL8)
- 调度核心业务:高并发异步队列 + In-Memory计算(自研)
该类服务器默认NUMA节点为2个,每个Socket一个节点,对应node0和node1。未做绑定时,线程容易从node0跨节点访问node1的内存,从而引发远程访问延迟(远高于本地访问)。
二、NUMA基础原理简述
NUMA(Non-Uniform Memory Access)结构下,不同CPU Socket各自拥有独立内存控制器,访问本地内存延迟较低,访问远程内存需通过QPI(Intel)或Infinity Fabric(AMD),延迟翻倍甚至更多。表现为:
| 访问方式 | 延迟 | 带宽 |
|---|---|---|
| 本地内存访问 | 低 | 高 |
| 跨节点内存访问 | 高 | 低 |
因此,线程与内存应固定在同一个NUMA节点上运行,避免“远程读写”。
三、NUMA调优实操步骤
1. 查看NUMA节点分布情况
lscpu | grep -i numa
输出示例:
NUMA node(s): 2
NUMA node0 CPU(s): 0-47
NUMA node1 CPU(s): 48-95
进一步查看内存分布:
numactl --hardware
确认node0、node1各自拥有512GB内存。
2. 绑定线程与内存到同一NUMA节点
最推荐的方法是使用 numactl 工具运行目标程序:
numactl --cpunodebind=0 --membind=0 ./my_worker
此命令的含义是:
- 仅使用NUMA node 0上的CPU
- 仅从NUMA node 0分配内存
如果需要在node1运行:
numactl --cpunodebind=1 --membind=1 ./my_worker
对我们自己的高并发计算进程,我们使用了 supervisor 配置如下:
[program:worker_node0]
command=/usr/bin/numactl --cpunodebind=0 --membind=0 /opt/app/worker
[program:worker_node1]
command=/usr/bin/numactl --cpunodebind=1 --membind=1 /opt/app/worker
这样确保每组worker进程只在本地节点执行。
3. 多进程/线程环境下的CPU亲和绑定
在更复杂的调度策略中,我们使用 taskset 或 sched_setaffinity 精确绑定线程到特定核心。例如,使用 taskset 启动绑定:
taskset -c 0-47 ./my_worker_node0
taskset -c 48-95 ./my_worker_node1
在程序内部使用 C/C++ 绑定线程到CPU:
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(core_id, &cpuset);
pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
核心id按NUMA节点划分,node0使用0-47,node1使用48-95。
4. 内存页迁移与热修复排查
某些情况下系统运行一段时间后,内存页可能被迁移到远程节点。可以用 numastat -p pid 检查每个进程内存分配状态。
如果发现 local_node 和 other_node 差异大,说明存在跨节点分配,建议重启进程并强制绑定内存。
也可用 mbind + mmap 技术在内核层直接控制分配内存页归属。
5. 其他系统级优化建议
设置进程调度策略为 SCHED_FIFO
struct sched_param param;
param.sched_priority = 80;
pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m);
避免Linux动态迁移线程造成跨NUMA漂移。
禁用Transparent Huge Page(THP)
echo never > /sys/kernel/mm/transparent_hugepage/enabled
THP可能造成内存页移动,不利于NUMA一致性。
四、性能验证与对比
我通过自研 benchmark 进行了绑定与未绑定的对比测试,结果如下:
| 测试项 | 未绑定NUMA | 正确绑定NUMA |
|---|---|---|
| 平均延迟(us) | 158 | 92 |
| 最大延迟(us) | 412 | 104 |
| 标准差(抖动性) | 39 | 8 |
| CPU利用率差异 | 10-15%浮动 | 接近满载 |
显而易见,正确绑定NUMA节点后,延迟下降超40%,抖动收敛接近5倍,从根本解决了业务突发延迟无法解释的问题。
NUMA架构并不是“高级功能”,而是双路服务器上的基本常识。一旦处理不当,将导致严重的性能抖动,甚至在高频交易、实时音视频、数据索引类系统中直接导致业务异常。通过 numactl、taskset、线程亲和设置,我们能有效管控CPU和内存亲缘关系,彻底规避NUMA跨节点访问带来的隐形性能杀手。
在香港的物理机环境下,这种级别的优化并不是“锦上添花”,而是“雪中送炭”。只有掌控住硬件亲缘特性,我们的业务才算真正吃满性能、稳如磐石。











