第80期 | 缺页时,内存才真正开始干活
上一期讲完翻译的小抄,这一期讲小抄查不到、页表里也没有的那一刻。程序以为内存已经在手边了,真正干活的时刻,恰恰是第一次碰到它的时候。
一、缺页异常,先占位后取货
malloc、mmap、brk 向内核要内存时,内核大多只记一笔账:在进程的地址空间里划出一段区间,页表里还没有现成的翻译,一页物理内存都没给。程序第一次读写那一页,CPU 查页表发现此路不通,抬出一个缺页异常,x86 上是 14 号异常,控制权回到内核。内核先查:这个地址在不在已划出的区间里?不在,送 SIGSEGV,程序段错误;在,再分两种:匿名页,只读时先指到同一张全零页,写时才真正分一页清零的物理页;文件映射的页,先去页缓存里找,找不到再从磁盘读进来¹。
这两种缺页的代价差着量级。页已经在内存里、只是页表没贴好的,叫小缺页,不碰磁盘;非得等磁盘的,叫大缺页,一次能慢上几千倍。一个大程序启动快,靠的正是这套懒办法:可执行文件映射进地址空间时并不全读进内存,执行到哪一页,缺页到哪一页,没执行过的代码,一辈子也不必进内存。
程序不是均匀用内存的,一段时间只碰一小簇页。Denning 在 1968 年把这簇叫工作集:在最近一段窗口里被碰过的页的集合²。如果所有在跑的进程的工作集加起来超过了物理页框,系统就开始来回倒页:刚换出去的页马上又要,CPU 的时间花在倒页上,不花在算上,这就是抖动。治法不是把页倒得更快,而是少接活:挂起几个进程,让留下来的工作集能住得下。页表的骗局到这里露了底:地址可以随便画,物理页不能凭空变出来。
二、记忆按需取,不按编号取
如果 AI 的操作系统先出现,记忆大概不会先有“地址”这个概念。智能体手头的上下文就是它的工作集:正在用的几段对话、几个文件、几条规则,窗口一滑,工作集就换了。缺页在智能体世界里的样子,是“想起一件事,发现上下文里没有”,系统得去外部记忆里取:向量库里按意思捞一段、文件里读一节、摘要里翻一页。PagedAttention 把分页的笔法直接用进了大模型的 KV cache³:序列逻辑上连续,物理上切成块,块随 token 增长按需分配,用完释放,缺页、换页那套账,在显存里重算了一遍。MemGPT 更进一步,把上下文窗口当主存、外部存储当磁盘,摘要换入换出就是换页⁴。
再推一格:未来的“缺页”可能连地址都没有,取东西靠算距离、算相似;“预取”也不再是顺序读下一页,而是猜下一步会想起什么。猜对了省时间,猜错了白占地方,和操作系统顺序预读下一页猜错一个样。抖动的账也一样:如果同时开的智能体太多,每个的工作集都住不下,系统就会在换入换出摘要之间空转,回答越来越慢,活却没多干。Denning 的治法原样适用:先让少数任务的工作集住下,再谈多开。还有一处不同,得先认账:页表换页是无损的,原样取回;上下文摘要换出再换回,细节已经丢了一轮,新记忆的“缺页”,取回来的不一定是原来那一页。
范式之眼也在这里:线性地址的缺页问的是“这一页在不在”,向量空间的缺页问的是“离得最近的是哪一段”,偏移算术失效了,缺页、预取、工作集这套逻辑还活着。
三、亲手看一次缺页
手边任何一台 Linux 机器,先数一数小缺页和大缺页:
/usr/bin/time -v python3 -c "pass" 2>&1 | grep -i "page faults"
输出里 Minor 一行是小缺页,Major 一行是大缺页。同一文件连读两次,第二次的大缺页会少很多:页已经缓存在内存里,缺页只是补一张页表,不用再等磁盘。
再看一次“占位不是拥有”。下面这段 Python 先映射 256MB,再逐页碰一遍,每步都打印进程自己的 VmSize 和 VmRSS:
python3 - <<'PY'
import mmap
def show(tag):
for line in open('/proc/self/status'):
if line.startswith(('VmSize', 'VmRSS')):
print(tag, line.strip())
show('映射前')
m = mmap.mmap(-1, 256 * 1024 * 1024)
show('映射 256MB 后')
for i in range(0, len(m), 4096):
m[i] = 1
show('逐页碰过后')
PY
你会看到:映射之后,VmSize 多了 256MB,VmRSS 几乎没动;逐页碰过之后,VmRSS 才跟着爬上去。没碰过的页,再大的数字也只是账面。
落到普遍场景,同一本账都能套。本地大模型服务上,KV cache 的块按需分配,就是缺页的显存版,块分得太碎,换页开销先吃掉一批算力;边缘小设备上,物理内存本来就小,工作集超了就抖动,部署前先算工作集,别先堆任务;视频 AI 平台里,一路视频的推理缓冲也可以当成“页”来管,告警触发时才换入,平常不必全占着;企业私有智能中心里,一个智能体的上下文预算先按工作集算清,再往下排任务,上下文换页就不是提示词技巧,而是内存管理。
觉 然 小 注
占位不是拥有。内存如此,记忆也是:没取到手、正在用的,才算数;只划在名下的一大片,碰都没碰过。
引 用 与 出 处
- [1] Robert Love. Linux Kernel Development. Addison-Wesley, 2010.
- [2] Denning, P. J. The Working Set Model for Program Behavior. Communications of the ACM, 1968.
- [3] Kwon, W. et al. Efficient Memory Management for Large Language Model Serving with PagedAttention. SOSP, 2023. https://arxiv.org/abs/2309.06180
- [4] Packer, C. et al. MemGPT: Towards LLMs as Operating Systems. arXiv:2310.08560, 2023. https://arxiv.org/abs/2310.08560
· · ·