这里说明 paged attention 的工作方式,并记录 minivLLM 的实现进度。
审计日期:2026-06-07。基于对 multimodal/minivLLM/ 的完整静态分析。
Paged attention 借鉴了操作系统虚拟内存的页表(page table)思想,将 KV cache 切分为固定大小的"页"(blocks)。 它可以解决 contiguous KV cache 的三大问题:
传统 contiguous buffer 为每个序列预分配 max_seq_len 的连续空间。
不同序列长度差异大时,短序列浪费容量,长序列可能需要动态移动数据。
序列从 100 token 增长到 200 token 时,contiguous buffer 可能需要将全部数据复制到新位置。 Paged attention 只需分配新 block,block table 追加 1 个新条目即可。
多个请求往往共享相同的 prefix(如 system prompt)。 Paged attention 允许多个 block table 指向相同的物理 block,避免重复存储,这就是 prefix caching。
minivLLM 当前使用的是 contiguous buffer,不是 paged attention。
KVCache 的形状为 (num_layers, max_seq_len, num_kv_heads, head_dim),是连续的一维分配,无 block table 间接寻址。
Paged attention 需要 5 个组件:Block 分配器、Block Table、Slot Mapping、Paged Attention Kernel 和调度器集成。 参考 vLLM 的 PagedAttention v1/v2 实现可作为学习指南。
utils/context.py 行 14)utils/context.py 行 12)config.py 行 12)从 contiguous buffer 改为 paged attention 的最小可用实现约 350 行 Python(纯 PyTorch,无 CUDA kernel):
| 模块 | 预估代码量 | 说明 |
|---|---|---|
| Block Manager | ~100 行 | 基于 Python list 的 block 分配/释放 |
| Block Table 管理 | ~50 行 | Tensor 形状 [batch_size, max_blocks_per_seq] |
| Paged Attention Matmul | ~50 行 | 逐个 block 做 \(QK^{\mathsf{T}}\),纯 PyTorch |
| 调度器 | ~150 行 | FIFO scheduler + prefill/decode 切换 |
前提条件:修复 B1(Attn 参数不匹配)和 B2(act_fn=None),将 KV cache 接入 forward。
minivLLM 的 Context 和 Config 中的 paged attention 字段与 vLLM 的 AttentionMetadata 接口设计一致,说明作者在规划时参考了 vLLM。当前这些字段仅为占位符,无实现。
| 组件 | 存在 | 状态 |
|---|---|---|
| Block Manager | 无 | 不存在 |
| Block Table(分配逻辑) | 无 | 不存在(字段定义存在但未使用) |
| Slot Mapping | 无 | 不存在(字段定义存在但未使用) |
| Paged Attention Kernel | 无 | 不存在(Attn.forward 为标准 MHA) |
| Scheduler 集成 | 无 | 不存在 |
| KVCache(当前实现) | 有 | Contiguous buffer,未接线 |
| Config 预留字段 | 有 | kvcache_block_size / num_kvcache_blocks |
| Context 预留字段 | 有 | block_tables / slot_mapping / context_lens |
| 特性 | Contiguous KV Cache | Paged KV Cache |
|---|---|---|
| 分配单位 | Token 级别(位置索引) | Block 级别(block_id 索引) |
| 内存碎片 | 严重(不同序列长度导致外部碎片) | 极少(统一 block 大小,内部碎片可控) |
| 多序列支持 | 困难(需每个序列独立分配连续空间) | 天然支持(block table 映射) |
| 动态增长 | 可能需数据迁移 | 只需分配新 block |
| Prefix Caching | 不支持 | 支持(多序列共享 block) |
| 实现复杂度 | 简单 | 中等(需 block manager + 间接寻址) |
| 内存布局 | [layer, pos, kv_head, head_dim] | [num_blocks, layer, block_size, kv_head, head_dim] |