You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何减少小页错误?为何每秒出现11K次minor-faults?

问题

我的程序二进制文件约3MB,可轻松放入L3缓存。我尽可能早调用mmap(及malloc),让操作系统有时间为程序准备好内存页。我还在多处尝试预分配更多内存以减少重分配,但程序运行1秒仍会产生11K次minor-faults。
我尝试使用perf工具结合fault和cache-misses事件来定位问题,但未能理清原因。多数错误似乎出现在一个64字节的结构体上。我为此编写了一个线性分配器,随后从malloc(未使用realloc)切换到mmap,甚至改为一次性分配全部所需内存,但仍存在11K次错误。
请问什么是minor-faults?为何我的程序每秒会产生11K次这类错误?

解答

什么是Minor Faults

Minor Fault(小页错误)是操作系统内存管理中的常见事件:当程序访问的虚拟内存地址已分配对应虚拟页,但该虚拟页尚未与物理内存页建立映射,或是暂存于内存页缓存而非磁盘交换分区时,操作系统会快速完成物理页映射,无需从磁盘读取数据,这个过程就叫minor fault。

它和Major Fault(大页错误)的核心区别是:后者需要从磁盘加载数据,耗时远高于minor fault,但minor fault累积过多同样会拖慢程序性能。

为什么你的程序会出现11K次Minor Faults

  • 内存懒加载机制:malloc、mmap这类调用仅向操作系统申请虚拟地址空间,默认采用懒加载策略——不会立即为虚拟地址分配物理内存页,只有当程序实际对这些地址执行读/写操作时,才会触发minor fault完成物理页映射。你提前调用mmap/malloc只是占了虚拟地址空间,若程序运行时才逐个访问这些内存页,自然会触发大量minor fault。
  • 写时复制(Copy-On-Write)触发:如果你的内存分配基于匿名映射(比如mmap(MAP_ANONYMOUS))或从系统共享零页初始化,第一次对这些内存执行写操作时,操作系统会触发minor fault,为你创建私有物理页副本以替换共享页映射。若你的64字节结构体大量分布在这类共享页上,每首次写入一个新页的结构体,就会触发一次fault。
  • 结构体的访问模式:若这个64字节结构体被大量实例化,且实例分散在多个虚拟内存页中(按4KB页大小算,每页可容纳64个结构体),当程序循环遍历或随机访问这些实例时,每触及一个未映射的新页,就会触发一次minor fault。哪怕你一次性分配了全部内存,只要这些页未被实际访问过,首次触碰就会触发fault。

验证与缓解思路

  • 用perf record -e minor-faults ./your_program结合perf report定位具体触发fault的代码路径,确认是否由结构体访问导致页映射触发。
  • 若想提前消除这些minor fault,可在预分配内存后主动遍历整个内存区域并执行写操作,强制操作系统完成所有物理页映射。示例代码:
#include <unistd.h>
#include <sys/mman.h>

char *buf = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (buf != MAP_FAILED) {
    size_t page_size = sysconf(_SC_PAGESIZE);
    for (size_t i = 0; i < total_size; i += page_size) {
        buf[i] = 0; // 触发写时复制,完成物理页映射
    }
}

内容的提问来源于stack exchange,提问作者Stan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 12:12:38