Skip to content

程序访问的虚拟地址尚未映射到物理内存时 CPU 触发的中断,内核借此按需加载页面,是虚拟内存的核心机制。

为什么需要了解 Page Fault

ds4.c 用 mmap 加载 81GB 模型文件,但不会一次性读入全部数据。程序访问某个地址时,如果对应的页面还在磁盘上,CPU 触发 page fault,内核把这一页从磁盘读入物理内存,然后恢复程序执行。

这意味着 page fault 的时机直接影响性能:

  • 推理中触发 → 延迟抖动(毫秒级停顿)
  • 预热阶段触发 → 集中承受延迟,推理时无卡顿

核心原理

程序访问 ptr[0]

    ↓  CPU 查页表: 这一页在物理内存中吗?

    ├─ 是 → 直接访问(纳秒级)

    └─ 否 → 触发 Page Fault

              ↓  内核从磁盘读入该页到物理内存
              ↓  更新页表映射
              ↓  恢复程序执行(重新访问 ptr[0],这次命中)

              ── 耗时约 1-10ms(取决于磁盘速度)

在 ds4.c 中的两种策略

1. 预热(主动触发 page fault)

c
// model_warm_weights(): 逐页强制触发 page fault
for (size_t i = 0; i < size; i += 4096) {
    volatile char c = ((char *)map)[i];  // 读每页首字节
}

启动时承受所有延迟,推理时不再卡顿。

2. 避免 calloc(延迟 page fault 的陷阱)

c
// xmalloc_zeroed(): 用 malloc + memset,不用 calloc
void *p = xmalloc(size);
memset(p, 0, size);  // 立即触发所有 page fault

为什么不用 calloc?Darwin 内核对大块 calloc 使用共享零页优化——首次写操作才触发 page fault。如果延迟到推理阶段,密集的 page fault 可能在 macOS 上触发内核 panic。

相关概念

  • mmap — mmap 的按需加载依赖 page fault 机制
  • xmalloc — xmalloc_zeroed 避免 calloc 的延迟 page fault 问题
  • gguf — GGUF 张量数据通过 mmap 按需加载

详见 Part 1 — 内存管理与 mmap