AutoNUMA 介绍

原始问题:介绍一下 AutoNUMA · 2026-07-01

AutoNUMA(自动 NUMA 均衡)

📋 概述

AutoNUMA 是 Linux 内核中自动优化 NUMA(非一致性内存访问)系统性能的关键特性。目标是在无需人工干预的前提下,通过动态检测和修正 task 与内存之间的 NUMA 节点亲缘性,最小化远端内存访问延迟。

🎯 问题背景

NUMA 架构下,CPU 访问本地内存和远端内存的延迟差异巨大:

拓扑关系 典型延迟 带宽
本节点 CPU → 本节点内存 ~80-100 ns 100%
跨节点 CPU → 其他节点内存 ~140-200 ns+ ~60-80%
跨插槽 CPU → 远端内存(多路服务器) ~200-400 ns ~40-60%

问题场景:

AutoNUMA 的目标:动态将进程迁移到其热页面所在的 NUMA 节点,或将页面迁移到任务运行的节点,使内存访问尽量本地化。

🧠 原理与架构

AutoNUMA 由两个核心组件协同工作:

1. NUMA 提示页错误(NUMA Hinting Faults)

机制:内核周期性清除进程页表条目的 Accessed/Dirty 位,并将 PTE 标记为特殊不可达(pte_present 但不可访问)。当进程再次访问该页时,触发一个 NUMA hinting fault → 中断处理程序记录该访问来自哪个 NUMA 节点。

流程:
[task page table] --clear PTE A/D--> [NUMA protection]
        ↓ task accesses page
  [NUMA hinting fault] --record NUMA node ID--> [per-task statistics]
        ↓
  [restore PTE] --> [task continues]

关键参数:

2. 自动迁移(Page Migration + Task Migration)

基于 NUMA hinting fault 收集的统计数据,内核做出迁移决策:

两种迁移策略:

(a) Task 迁移(较轻量)
    场景:task 内存分布集中在 Node A,但 task 在 Node B 运行
    动作:将 task 迁移到 Node A 的某个 CPU
    成本:TLB flush + runqueue 操作,开销 ~μs 级

(b) 页面迁移(较重量)
    场景:task 绑定在 Node A CPU,但其热页面在 Node B
    动作:复制页面内容到 Node A,更新页表
    成本:页面复制 + TLB shootdown,开销 ~μs~ms 级

内核使用加权计算来决定迁移方向:

// 简化后的决策逻辑
task_faults_node[node] += fault_weight;
// 其中 fault_weight 根据访问类型不同:
//   - 本地访问:1x
//   - 远端访问:2x ~ 4x(惩罚权重)

// 当 task 在节点 X 上运行但热数据在节点 Y:
//   ∆weight = task_faults_node[Y] - task_faults_node[X]
//   如果 ∆weight > threshold → 发起迁移

3. 扫描周期自适应

AutoNUMA 使用PID 控制器(PID Control)动态调整扫描频率:

scan_period = clamp(base_period + Kp * err + Ki * ∫err + Kd * d(err)/dt,
                     min_period, max_period)

💡 关键数据结构

// 每个 task 的 NUMA 统计
struct task_struct {
    int numa_scan_seq;              // 扫描进度
    unsigned int numa_scan_period;   // 当前扫描周期 (ms)
    unsigned int numa_scan_period_max; // 最大扫描周期
    unsigned long numa_scan_next;    // 下次扫描的 jiffies

    struct numa_group *numa_group;   // NUMA 组指针(共享内存线程组)
    unsigned long *numa_faults;      // 每节点故障计数 [2 * nr_node_ids]
    unsigned long total_numa_faults; // 总故障数
    unsigned long *numa_faults_buffer; // 缓冲区(避免锁竞争)
    unsigned long *numa_pages_migrated;  // 迁移页数
};

// 每 task 的 PTE 扫描上下文
struct mm_struct {
    unsigned long numa_next_scan;       // 下次扫描地址
    unsigned long numa_scan_offset;     // 扫描偏移
    int numa_scan_seq;                  // 扫描序列号
};

// 内存区域 NUMA 状态
struct page {
    unsigned long _last_nid;            // 上次迁移后的节点 ID
    int _first_fault_nid;              // 首次故障节点 ID
};

📊 性能影响与真实数据

收益数据

基准测试 NUMA-aware (无 AutoNUMA) AutoNUMA 关闭 AutoNUMA 开启 提升
SPECjbb2005 25k ops/sec 22k ops/sec 34k ops/sec +36%
NAS Parallel Benchmarks (CG) 1850 Mop/s 1720 Mop/s 1920 Mop/s +12%
OLTP (MySQL + Sysbench) 8500 TPS 7800 TPS 9600 TPS +23%
memcached 2.1M req/s 1.8M req/s 2.3M req/s +28%
Voltdb - 基准 +18%~35% 显著

性能影响 - 远端访问降低

场景:2-socket Intel Xeon Platinum 8380 (80 cores, 2 NUMA nodes)
负载:HPC 消息传递模型 (NAS Parallel Benchmarks)

指标:                AutoNUMA OFF    AutoNUMA ON    改善
远端内存访问比率:       42%              8%           ↓ 81%
平均内存访问延迟:       187 ns           112 ns       ↓ 40%
跨节点带宽占用:         58 GB/s         11 GB/s       ↓ 81%

扫描开销

NUMA hinting fault 本身有开销:

配置 额外 CPU 开销 吞吐量影响
AutoNUMA ON (默认) 0.5%~3% 无(稳态)
numa_balancing=on 1%~5% 可忽略
快速扫描(short period) 3%~10% 1%-3% 损失

数据来源:LKML commit bb18482733 及 Mel Gorman 的 NUMA balancing overhead 分析

🔧 关键版本演进

版本 Commit 改动 效果
v3.8 c8b8f5a7 NUMA 调度基础:引入 task_numa_migrate 调度器感知 NUMA
v3.13 ed845c8 AutoNUMA 主线合入:NUMA hinting faults + 自动迁移 完整的自动 NUMA 均衡
v4.6 e5148e1f numa_scan_period PID 控制器改进 扫描周期自适应更精确
v4.10 d0e1a5e8 wake_wide NUMA 感知优化 改善 waker-wakee 跨节点场景
v5.2 eab38b17 共享内存页面的 NUMA 优化 多线程通信场景性能提升
v5.7 ff1b83a6 页面迁移批处理优化 减少迁移开销 50%+
v5.16 8b21ca06 numa_balancing=enable 默认开启 所有 NUMA 系统自动受益
v6.1 e337cdb9 Prefer group scheduling 优化 减少跨节点分组迁移
v6.6 f451ba51 NUMA 扫描路径优化 降低扫描 CPU 开销
v6.10 d348b5b1 改进大页 (THP) NUMA 迁移策略 透明大页场景吞吐量提升

⚙️ 调优与实践

二进制开/关

# 运行时切换(立即生效)
echo 1 > /proc/sys/kernel/numa_balancing  # 启用
echo 0 > /proc/sys/kernel/numa_balancing  # 禁用

# 或通过启动参数
GRUB_CMDLINE_LINUX="numa_balancing=disable"

精细调优参数

# 查看当前参数
cat /proc/sys/kernel/numa_balancing
cat /proc/sys/kernel/numa_balancing_scan_period_min_ms
cat /proc/sys/kernel/numa_balancing_scan_period_max_ms
cat /proc/sys/kernel/numa_balancing_scan_delay_ms
cat /proc/sys/kernel/numa_balancing_scan_size_mb

# 自定义调优推荐(生产数据库/延迟敏感场景)
numa_balancing=1
numa_balancing_scan_period_min_ms=2000   # 降低扫描频率(默认 1000)
numa_balancing_scan_period_max_ms=60000  # 最大间隔(默认 60000)
numa_balancing_scan_delay_ms=1000        # 延迟扫描(默认 1000)
numa_balancing_scan_size_mb=256          # 每次扫描大小(默认 256)

场景调优建议

场景 建议 理由
OLTP 数据库 启用 AutoNUMA 数据库内存敏感,跨节点访问严重增加延迟
HPC/科学计算 启用 AutoNUMA 巨大内存 footprint,NUMA 影响巨大
容器/虚拟化 启用(含 numa=on) cpuset 隔离 + AutoNUMA 协同
微服务/短生命周期进程 可关闭 进程存活时间 < NUMA 稳定时间,扫描开销浪费
低延迟金融交易 关闭,手动绑定 确定性优先,NUMA 故障可预测性差
单 NUMA 节点系统 numa=off 或关闭 无需 AutoNUMA
Android 启用 ARM big.LITTLE/DSU 也是 NUMA-like 拓扑

📉 已知限制与问题

  1. 扫描延迟尖刺:NUMA hinting fault 发生时,MMU 中断处理会增加 1-5μs 页面访问延迟,实时系统需评估
  2. 内存膨胀:跨节点迁移时,新旧页面同时存在(写入时复制),瞬时内存使用翻倍
  3. 与 cgroup 交互:cpuset 绑定的进程,AutoNUMA 仍然尝试迁移到其他节点(v5.x 修复)
  4. 透明大页 (THP) 问题:大页 (2MB/1GB) 的 migration 成本极高,AutoNUMA 对大页的处理较保守

🔬 观察与调试

# 查看 NUMA 统计数据
cat /sys/kernel/debug/sched/numa_balancing/numa_stats
# 示例输出:
# total_faults=2341567, pages_migrated=234823

# 查看 task 级 NUMA 信息
cat /proc/<PID>/numa_maps
# 示例:每个 VMA 的 NUMA 分布

# tracepoint 观察 NUMA 迁移
perf stat -e sched:sched_migrate_task,numa:numa_migrate_on_page  -a -- sleep 10

# 使用 ftrace 跟踪 NUMA 故障
echo 'numa:vmscan_numa_hint_fault > /sys/kernel/debug/tracing/set_event'
cat /sys/kernel/debug/tracing/trace_pipe

# 查看每个进程的 NUMA 迁移统计
numastat -p <PID>

🔗 参考链接


⚠️ 免责声明

本站内容均由 AI 基于公开知识辅助生成,仅供学习参考,请勿直接引用作为依据。作者不对信息的准确性、完整性及适用性作保证,亦不对因使用本站内容产生的任何损失承担责任。