﻿# numastat —— 按 NUMA 节点看内存分配命中/未命中

## 这个工具是做什么的

`numastat` 是排查 NUMA 内存问题的关键：它告诉你内存分配**有多少落在了本地节点（命中）、多少被迫去了远程节点（未命中）**，以及某个进程的内存具体分布在哪些节点。属 `numactl` 包。原理见 [numa.md](/concepts/numa/numa.md)。

## 可以回答什么问题

| 问题 | 怎么用 |
|------|--------|
| 整机远程内存访问多不多？ | `numastat` 看 `numa_miss` / `other_node` 占比 |
| 远程访问是持续发生还是历史累计？ | `watch -d -n 1 numastat` 看增量变化 |
| 某个进程的内存在哪个节点？分布均匀吗？ | `numastat -p <PID>` 看各节点分布 |
| 进程内是哪段内存在远程节点？ | `cat /proc/<PID>/numa_maps` 看每段内存的 `N0=... N1=...` |

## 数据来源

- **整机统计**：读每个节点的 `/sys/devices/system/node/node<N>/numastat`（内核维护的 per-node 分配计数：numa_hit/numa_miss/…）。
- **按进程（`-p`）**：读 `/proc/<pid>/numa_maps`，把该进程各内存段落在哪个节点汇总成表。
- **由此决定的特性**：整机计数是**开机以来的累计值**，要判断"现在"的趋势得**隔一段时间看两次做差**（或看增长速度），单看一次只是历史总量。

## 一、整机节点统计

```bash
numastat
```

```bash
                           node0           node1
numa_hit              1203847113       998172340
numa_miss                 402118        15872044
numa_foreign            15872044          402118
interleave_hit             18324           18201
local_node            1203201847       997903122
other_node                645266          269218
```

**核心字段（判断远程访问多不多）**：

| 字段 | 含义 | 判读 |
|------|------|------|
| `numa_hit` | 想在本节点分配、**且成功**的次数 | 越高越好（本地访问）|
| `numa_miss` | 想在本节点分配、**但本地不够被迫分到别处** | **持续增长 = 本地内存不足，产生远程访问** |
| `numa_foreign` | 本想分到别的节点、结果落到了本节点 | 与别人的 miss 对应（A 的 miss 常是 B 的 foreign）|
| `local_node` | 进程在本节点运行且内存也在本节点 | 越高越好 |
| `other_node` | 进程在本节点运行但内存在别的节点 | **高 = 远程访问多，是要优化的信号** |
| `interleave_hit` | interleave 策略成功分配数 | 用了 `--interleave` 才有意义 |

> 判断法：`numa_miss` / `other_node` 相对 `numa_hit` / `local_node` 占比高，或**两次采样间它们增长快**，说明有大量远程内存访问，该考虑绑定（[numactl.md](/tools/numa/numactl.md)）或调整内存分配方式。

## 二、按进程看内存分布（最实用，`-p`）

```bash
numastat -p <PID>            # 或 numastat -p 进程名
```

```bash
Per-node process memory usage (in MBs) for PID 4043 (myapp)
                           Node 0          Node 1           Total
Huge                         0.00            0.00            0.00
Heap                       512.30           18.44          530.74
Stack                        0.13            0.00            0.13
Private                    820.11          402.55         1222.66
-----------------  --------------- --------------- ---------------
Total                     1332.54          420.99         1753.53
```

一眼看出该进程的内存在两个节点的分布。**如果进程主要跑在 Node0，却有几百 MB 落在 Node1**（如上例 `Private` 有 402MB 在 Node1）→ 这部分是远程访问，是优化目标。理想情况是内存集中在它运行的那个节点。

## 三、按内存类型汇总（`-m`、`-c`）

```bash
numastat -m                  # 各节点的 meminfo 式明细（MemFree/FilePages/AnonPages...）
numastat -c                  # 紧凑输出，节点多时好看
numastat -z                  # 隐藏全 0 的行，聚焦有数据的
```

## 四、看"现在"而非"累计"：两次做差

整机 `numastat` 是累计值，想看当前速率：

```bash
watch -d -n 1 numastat       # 每秒刷新，-d 高亮变化的数字，直接看哪列在涨
# 或手动隔几秒跑两次，对比 numa_miss/other_node 的增量
```

`numa_miss`、`other_node` 在负载期间**快速增长** = 此刻正在大量远程分配，比看累计绝对值更能定位实时问题。

## 五、关键指标速查

| 指标 | 缩写/英文 | 正常值 | 异常信号 |
|------|----------|--------|---------|
| 本地命中 | `numa_hit` / NUMA Hit | 越高越好 | — |
| 本地未命中 | `numa_miss` / NUMA Miss | 接近 0 | 持续增长 → 本地内存不足 |
| 外来分配 | `numa_foreign` / NUMA Foreign | 与别的节点 miss 对应 | — |
| 本地运行+本地内存 | `local_node` / Local Node | 越高越好 | — |
| 本地运行+远程内存 | `other_node` / Other Node | 0 或极少 | 占比高 → **需要 NUMA 优化的关键信号** |
| interleave 命中 | `interleave_hit` | 用了 interleave 才关注 | — |
| 进程各自节点分布 | Per-process Node Distribution | 集中在运行节点 | 大量内存在其他节点 → 远程访问 |

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

### 6.1 判断整机是否有远程访问问题

```bash
# 两步做差看趋势
numastat → 记录 other_node 的基数
# 跑一段负载后
numastat → 对比 other_node 增量
  → other_node 增长快 → 存在远程访问
    → numastat -p <PID> → 逐个进程看谁的内存跨节点
```

### 6.2 定位哪个进程在跨节点

```bash
numastat -p <PID> → 看各节点分布
  → 进程跑在 node0 但内存 40% 在 node1 → 大量远程访问
    → cat /proc/<PID>/numa_maps | grep N1= | head
      → 找出落在 node1 的具体内存段
        → 该段是堆还是 mmap? 是否需要在 node0 重新分配
```

### 6.3 实时监控远程访问速率

```bash
watch -d -n 1 numastat       # -d 高亮变化，盯住 other_node 列
# other_node 负载期间快速增长 → 此刻正在大量远程分配
```

### 6.4 确认绑定是否生效

```bash
# 绑前
numastat → other_node 有值
# 绑后（numactl --cpunodebind=0 --membind=0）
numastat -p <PID> → 所有内存都在 Node 0
# 其他节点列全为 0 → 绑定生效
```

## 七、交叉引用

`numastat -p` 说"有多少在远程节点"，要落到"是哪块内存"：

```bash
cat /proc/<PID>/numa_maps    # 每个映射段：地址 + 策略 + 各 node 上的页数(N0=.. N1=..)
```

例如某行 `... N0=12000 N1=45000` 表示这段内存大部分页在 node1——若进程跑在 node0，就是它。

## 六、结合本仓库

```bash
numactl -H                          # 先确认是 NUMA 机器
./cpu_demo &                        # 选个场景跑起来
numastat -p $(pgrep cpu_demo)       # 看它内存落在哪个节点（小程序基本集中在一个节点）
numastat                            # 看整机 numa_hit/miss
```

> ⚠️ 单节点机器上 `numastat` 只有一列 node0、miss 恒为 0——没有远程访问可言。这些指标只在多路 NUMA 机器上才有分析价值。

> 相关：概念与优化思路 [numa.md](/concepts/numa/numa.md)；绑定进程/内存到节点 [numactl.md](/tools/numa/numactl.md)。

