﻿# tlb-thrashing — perf 分析指南

> 用 `perf stat` 和 `perf record` 定量测量 TLB 行为，观察不同步长 D 下 `dTLB-load-misses` 的变化规律。

## 准备工作

```bash
cd demos/tlb-thrashing
make          # -O0 -g
```

## 核心事件：dTLB-loads / dTLB-load-misses

| 事件 | 含义 |
|------|------|
| `dTLB-loads` | 数据 TLB 查询总次数 |
| `dTLB-load-misses` | 数据 TLB 未命中次数（需 page walk） |
| `dTLB-load-misses / dTLB-loads` | D-TLB miss 率 |

**一次 TLB miss 的代价**：需要硬件 page walk 遍历 4 级页表 = 4 次内存访问，约 50~200 cycles。

## 实验 1: 不同步长的 TLB miss 率变化

tlb-thrashing 的核心实验原理：固定迭代次数 N=8192，增大步长 D，观察页工作集随 D 增大的过程。

| 步长 D | 页工作集 8D | 预期行为 |
|:---:|:---:|------|
| 1~4 | 8~32 页（32~128KB） | < L1 dTLB 容量，miss 率极低 |
| 8~32 | 64~256 页 | 接近或超过 L1 dTLB，miss 率开始上升 |
| 64~128 | 512~1024 页 | 超过 L1 dTLB 但 < L2 STLB，L1 miss 但 STLB 兜住 |
| 256+ | 2048+ 页 | **超过 L2 STLB**，每次回来都被挤光，miss 率飙升 |

```bash
# 一键对比
for d in 4 8 16 32 64 128 256 512 1024; do
    echo "=== D=$d (页工作集=$((8*$d))) ==="
    perf stat -e cycles,instructions,dTLB-loads,dTLB-load-misses \
        ./tlb_thrashing $d 2>&1 | grep -E '^\s|TLB'
done
```

### 预期结果

```bash
D=4   (32页):   dTLB-load-misses ~0       — L1 dTLB 全覆盖
D=16  (128页):  dTLB-miss 率 <5%          — L1 接近边缘
D=64  (512页):  dTLB-miss 率 ~10~20%      — 越过 L1，STLB 兜住
D=256 (2048页): dTLB-miss 率 ~50~80%      — STLB 也被挤爆
D=1024(8192页): dTLB-miss 率 >90%         — 全面 thrashing
```

**关键洞察**：自增次数一样（都是 8192 次），吞吐下降了不是计算变慢了，而是每次访问前多了一个 50~200 cycles 的"翻译过路费"。

## 实验 2: 精确定位拐点

```bash
# 全量扫表 D=1→1024，程序自动检测 L1 和 L2 拐点
./tlb_thrashing
```

输出：
```bash
   D  | 数组(KB) | 页工作集 | 吞吐(10⁹/s) | 相对(%)
------+----------+----------+-------------+--------
    1  │       32 │        8 │     12.345 │ 100%
    ...
   32  │     1024 │      256 │      8.234 │  67%  ← L1 拐点
   ...
  256  │     8192 │     2048 │      2.345 │  19%  ← L2 拐点 (thrashing)
```

## 实验 3: `perf record` 采样 TLB miss

```bash
# 用 -e 指定采样 dTLB-load-misses 事件
perf record -e dTLB-load-misses -g ./tlb_thrashing 512
# 看哪些代码在触发 TLB miss
perf report --stdio
```

预期：热点在 `bench_strided` 的内层循环 `a[i*stride]++` ——这正是跨页访问的地方。

## 实验 4: IPC 随 D 增大的变化

TLB miss 直接影响 IPC（因为每条访存指令都要等 page walk 完成）：

```bash
for d in 4 16 64 256 512 1024; do
    echo "=== D=$d ==="
    perf stat -e cycles,instructions ./tlb_thrashing $d 2>&1 |
        grep -E 'cycles|instructions|insn'
done
```

| D | IPC | 说明 |
|:--:|:--:|------|
| 4 | ~1.0 | 无 TLB 开销 |
| 64 | ~0.7 | 开始受影响 |
| 256 | ~0.3 | STLB 被挤爆 |
| 1024 | ~0.15 | 几乎每次访问都在 walk |

## dTLB vs iTLB

如果不仅要看数据 TLB，还要看指令 TLB（代码段碎片化场景）：

```bash
perf stat -e dTLB-loads,dTLB-load-misses,iTLB-loads,iTLB-load-misses \
    ./tlb_thrashing 64
```

tlb-thrashing 的代码很短（< 1 页），所以 `iTLB-load-misses` 应接近 0。

---

## 关联文档

- [concepts/cache/tlb.md](/concepts/cache/tlb.md)——TLB 原理深度文档
- [tools/code/perf.md](/tools/code/perf.md)——perf 工具主文档，§六.1.5 TLB 事件
- [concepts/tools/perf-demos-architecture.md](/concepts/tools/perf-demos-architecture.md)——perf 12 场景学习架构总纲

> **一句话**：TLB miss 率随数据集的页工作集呈阶梯式上升——关键拐点对应 L1 dTLB 和 L2 STLB 的物理容量，用 `perf stat -e dTLB-load-misses` 精确测量。

