﻿# numactl —— 查看 NUMA 拓扑 & 把进程/内存绑到指定节点

## 这个工具是做什么的

`numactl` 是 NUMA 调优的主力：既能一览机器的 NUMA 拓扑，又能在启动进程时**指定它跑在哪些核、内存分配到哪个节点**，从而消除远程内存访问。属 `numactl` 包（`yum install numactl` / `apt install numactl`）。原理见 [numa.md](/concepts/numa/numa.md)。

## 可以回答什么问题

| 问题 | 怎么用 |
|------|--------|
| 这台机器是 NUMA 架构吗？几个节点？ | `numactl --hardware`：`available: 2 nodes` = 是 NUMA |
| 每个节点有哪些 CPU 核？内存多大？ | `numactl -H` 看 `node N cpus` 和 `size` |
| 远程内存访问比本地慢多少？ | `numactl -H` 的 `distances` 矩阵（如 21 vs 10 = 约 2 倍） |
| 怎么把进程钉在某个 NUMA 节点？ | `numactl --cpunodebind=0 --membind=0 ./app` |
| 怎么只绑 CPU 不绑内存？ | `numactl --preferred=0`（软偏好） vs `--membind=0`（硬绑定） |
| 已运行的进程当前是什么 NUMA 策略？ | `numactl --show -p <PID>` |

## 数据来源

- **查询拓扑**：读内核 `/sys/devices/system/node/`（节点数、每节点 CPU 列表 `cpulist`、内存 `meminfo`、节点间距离 `distance`）。
- **施加绑定**：通过 `set_mempolicy(2)` / `sched_setaffinity(2)` 等系统调用给进程设置 NUMA 内存策略和 CPU 亲和性，再 `exec` 目标程序。
- **由此决定的特性**：绑定是**在进程启动时施加**并被子进程继承；`--hardware` 是纯查询、不改任何东西。

## 一、查看拓扑

```bash
numactl --hardware       # 或 -H：节点数、每节点的核与内存、节点间距离矩阵
numactl --show           # 或 -s：显示当前进程生效的 NUMA 策略（绑在哪、用什么策略）
```

`numactl -H` 输出（双路机示例）：

```bash
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9
node 0 size: 32143 MB
node 0 free: 10547 MB
node 1 cpus: 10 11 12 13 14 15 16 17 18 19
node 1 size: 32254 MB
node 1 free: 28741 MB
node distances:
node   0   1
  0:  10  21
  1:  21  10
```

看点：几个 node、每个 node 有哪些**核编号**（绑定时要用）、每个 node 的 `size`/`free`（内存是否均衡）、`distances` 矩阵（远程访问相对代价，本地=10）。`available: 1 nodes` 就是非 NUMA 机器，不用往下看了。

## 二、绑定进程（核心用法）

启动程序时把它约束到指定节点：

```bash
# CPU 和内存都绑到 node 0（最常用：彻底本地化，无远程访问）
numactl --cpunodebind=0 --membind=0 ./myapp
# 简写：-N=cpunodebind，-m=membind
numactl -N 0 -m 0 ./myapp
# 内存就近分配到"运行所在的那个节点"（不硬绑到固定 node，跟着 CPU 走）
numactl --localalloc ./myapp          # 或 -l
# 只绑内存到 node1，CPU 不限
numactl --membind=1 ./myapp
# 绑到具体几个物理 CPU 核（而非整个 node）
numactl --physcpubind=0-9 ./myapp     # 或 -C 0-9
# 内存优先用 node0，不够再溢出到别处（软偏好，不像 membind 那样硬失败）
numactl --preferred=0 ./myapp
```

### 几种内存策略的区别

| 选项 | 语义 | 内存不够时 |
|------|------|-----------|
| `--membind=N` / `-m` | **强制**只用节点 N 的内存 | 分配失败/触发回收，**不会**用别的节点 |
| `--preferred=N` | **优先**用节点 N，可溢出 | 溢出到其他节点（远程，但不失败）|
| `--localalloc` / `-l` | 就近分配到运行所在节点 | 跟着线程跑的节点走 |
| `--interleave=all` / `-i` | 在多个节点**轮流**分配 | 均摊带宽，适合大内存、访问无局部性的负载（如大型内存数据库）|

> 典型选择：**延迟敏感、有明显数据局部性** → `--cpunodebind + --membind`（绑死一个节点）；**吞吐型、访问遍布全内存**（如某些 in-memory DB、HPC）→ `--interleave=all`（把带宽摊到所有节点，避免单节点内存控制器成瓶颈）。

## 三、验证绑定生效

```bash
numactl --cpunodebind=0 --membind=0 ./myapp &
numactl --show -p $(pgrep myapp)        # 看它实际的策略
numastat -p $(pgrep myapp)              # 看内存是否真落在 node0（见 numastat.md）
cat /proc/$(pgrep myapp)/numa_maps | head   # 每段内存落在哪个 node
taskset -cp $(pgrep myapp)              # 看 CPU 亲和性（绑到哪些核）
```

## 四、关键指标速查

| 指标 | 缩写/英文 | 命令 | 正常值 | 异常信号 |
|------|----------|------|--------|---------|
| NUMA 节点数 | Node Count | `numactl -H` `available:` | ≥ 2 才需要关心 | 1 节点 → 非 NUMA，无需优化 |
| 节点间距离 | Node Distance | `numactl -H` `distances` | 本地=10，远程≤21 | 远程 > 20 → 跨节点延迟是本地 2 倍+ |
| 每节点可用内存 | Node Free Memory | `numactl -H` `free` | 均衡 | 某节点接近 0 → 新分配必然走远程 |
| 进程当前策略 | Current Policy | `numactl --show` | `default` | `membind=1` 但 CPU 在 node0 → 跨节点 |
| 内存是否绑在运行节点 | NUMA Hit Ratio | `numastat -p <PID>` | 内存集中在运行节点 | 大量内存在其他节点 → 远程访问 |

## 五、常见排查方法与分析

### 5.1 确认是否需要 NUMA 优化

```bash
numactl -H → available: 1 nodes → 不需要，只有一个节点
  → 2 nodes 以上 → 先看当前是否有跨节点访问
    → numastat → other_node 是否持续增长
    → numastat -p <PID> → 进程内存是否跨节点
```

### 5.2 延迟敏感应用 → 绑死一个节点

```bash
# 确认网卡/GPU/加速器在哪，绑到同节点
numactl -H → 确认拓扑
numactl --cpunodebind=0 --membind=0 ./my_latency_app
# 验证生效
numactl --show -p <PID>         # 看策略
numastat -p <PID>               # 看内存分布
taskset -cp <PID>                # 看 CPU 亲和性
```

### 5.3 吞吐型应用 → interleave 均摊带宽

```bash
# 大内存数据库/内存池，访问模式无局部性
numactl --interleave=all ./my_db
# 验证：各节点分配应均匀
numastat -p <PID>
# interleave_hit 在各节点都应非零
```

### 5.4 验证绑定效果

```bash
# 绑前
numastat -p <PID> → Node0: 800MB, Node1: 400MB（有远程）
# 绑后（--cpunodebind=0 --membind=0）
numastat -p <PID> → Node0: 1200MB, Node1: 0MB（全部本地）
```

## 六、交叉引用

- `taskset` 只管 **CPU 亲和性**（把进程钉在哪些核），不管内存。
- `numactl` 同时管 **CPU 节点 + 内存策略**，是 NUMA 场景更完整的工具。
- 只想绑核不碰内存策略用 `taskset -c 0-9 ./app`；要连内存一起本地化用 `numactl`。

> CPU 亲和性(绑核)本身——为什么绑、taskset/sched_setaffinity/pthread 三层接口、绑核 vs 绑节点、isolcpus/中断亲和性、以及绑错更慢的坑——详见 [../process/thread-affinity.md](/concepts/process/thread-affinity.md)。`numactl --cpunodebind` 是"绑到某节点的那组核"(节点级),更细的"钉到单个核"用那篇的 taskset/pthread 接口。

## 五、结合本仓库

```bash
numactl -H                                        # 确认是不是 NUMA 机器（几个 node）
numactl --cpunodebind=0 --membind=0 ./cpu_demo    # 绑到 node0 跑
numactl --show                                    # 看默认策略
```

> ⚠️ 单节点机器（`available: 1 nodes`）上绑定没有意义——只有一个节点，怎么绑都是本地。NUMA 优化只在多路服务器上才有价值。绑定也可能**降低灵活性**（调度器不能把线程迁到空闲的其他节点），需结合负载实测，别无脑绑。

> 相关：概念与排查思路 [numa.md](/concepts/numa/numa.md)；节点内存统计 [numastat.md](/tools/numa/numastat.md)。

