vToken:给 LLM 的 KV 缓存装上”虚拟内存”
一个让推理工程师夜不能寐的数字
你正在给一个 13B 参数的 LLaMA 模型做推理服务。模型权重占 26 GB,看起来还能接受。但当上下文拉到 32K token 时,KV 缓存一个人就吃掉 25 GB——每个 token 0.8 MB,3.2 万个 token 就是 25.6 GB。GPU 80 GB 的显存,被一个长上下文请求的 KV 缓存吃掉三分之一。
这不是新闻。每个做 LLM 推理的人都知道 KV 缓存是内存瓶颈。所以社区发明了 PagedAttention(vLLM 的核心),把 KV 缓存切成固定大小的块(block,通常 16 token/块),像操作系统的分页机制一样管理。又发明了一堆 token 级驱逐算法——H2O 留注意力分数最高的”重击手”token,StreamingLLM 留注意力沉槽+最近窗口,Scissorhands 留持续注意力模式的 token。
听起来问题解决了?没有。问题才刚刚开始。
被忽视的”颗粒度错配”
来自国防科技大学、北京大学的研究团队在 2026 年 8 月的论文 vToken 中,精确地测量了一个所有人都遇到过但没人正式定义的问题:
> 驱逐策略在 token 粒度做决策,运行时在 block 粒度做内存管理。这两个粒度不匹配。
什么意思?假设一个 block 有 16 个 token 位置。H2O 算法说”这个 block 里有 10 个 token 不重要,驱逐掉”。但 block 不能释放——因为里面还有 6 个活着的 token。这 10 个被驱逐的 token 留下的”洞”,变成了块内碎片(intra-block fragmentation)。
研究团队在 vLLM 上跑 H2O 驱逐算法,16K 上下文,batch size 16。结果触目惊心:
– 大多数已分配的 block 利用率不超过 50% – 块内浪费率 F 达到 40%-60%
也就是说,你辛辛苦苦用 H2O 算法驱逐了”不重要”的 token,省下的逻辑空间有 40%-60% 被困在”半死不活”的物理块里,完全无法被其他请求复用。
这就像你把一个 16 人间的办公室里 10 个人裁掉了,但办公室不能退租(还有 6 个人在里面),那 10 个空工位就这么浪费着。驱逐算法做了它该做的事,但运行时无法把”逻辑上的空闲”转化为”物理上的可回收内存”。
vToken 的核心洞察:加一层虚拟化
解决方案不是改驱逐算法,也不是缩小 block 大小(那只是把同样的碎片化换个位置)。vToken 的核心洞察是:
> 在 block 级运行时之上,加一层 token 级的虚拟化边界。
如果你做过操作系统,这个概念你应该很熟——这就是虚拟内存。
操作系统的虚拟内存做了什么?它把”进程看到的连续地址空间”和”物理内存的页框”解耦。进程以为自己有 4 GB 连续内存,实际上物理内存可能只有 2 GB,而且散落在不连续的页框里。中间靠页表(page table)做映射。
vToken 做了完全一样的事,只不过映射的对象是 KV 缓存:
| 操作系统虚拟内存 | vToken |
|---|---|
| 进程的虚拟地址空间 | 请求的逻辑 token 序列 |
| 物理页框 | PagedAttention 的物理 KV 块 |
| 页表 | Token Table |
| 缺页中断 + 页面回收 | 物理回收后端 |
| 进程不需要知道物理布局 | 驱逐策略不需要知道 block 布局 |
Token Table:KV 缓存的”页表”
vToken 为每个请求维护一张 Token Table,记录每个逻辑 token ID 的: – 物理位置:(block ID, offset) – 存活位:alive 或 dead
驱逐策略调用 evict_token(req_id, token_id) 时,只更新这张表的存活位——不移动任何 KV 数据。这就像操作系统标记一个页为”无效”但不立即把页框还给物理内存一样。
Token Table 的元数据开销极小:16K token 的序列只需 256 KB(每条目 16 字节),不到 7B 模型 FP16 KV 缓存足迹(约 2 GB)的 0.1%。
物理回收后端:异步”碎片整理”
标记完死 token 后,怎么把半死的 block 变成可回收的空闲 block?vToken 的回收后端做四件事:
1. 回收资格检查:扫描 block 利用率,找出低于阈值的”低效 block” 2. 余量感知准入:检查是否有足够的空闲 block 作为搬迁目的地。没有就等——不能为了回收而制造新的碎片 3. 搬迁规划:把活 token 从低效 block 搬到目标 block,贪心打包 4. 异步拷贝:在独立的 CUDA stream 上执行 KV 数据拷贝,不阻塞解码主流程
关键设计:拷贝是异步的。搬迁计划提交后,KV 数据在后台 stream 上拷贝,主解码 stream 继续工作。下一次 attention 计算前,用 CUDA event 确保相关拷贝完成,然后刷新 slot 映射。整个过程没有全局同步点。
这就像操作系统的页面整理(compaction)——把散落在各处的活页面搬到连续区域,腾出完整的空闲页框。只不过这里搬的是 KV tensor,在 GPU 上异步执行。
三个设计挑战的解法
vToken 解决了三个核心技术挑战:
C1:双视图一致性。 Token 可能独立于邻居死亡,但 block 只有在所有 KV 条目都搬走后才能释放。解法:Token Table 同时记录 token 级存活状态和 block 级占用率,两个视图通过同一张表维护。
C2:解码中的安全回收。 搬迁 KV 条目会改变 attention 读取的物理 slot。如果搬迁进行到一半,attention 读到一半新一半旧的数据,结果就错了。解法:slot 映射在搬迁完成后原子更新,attention kernel 只看 slot 映射,不直接访问物理位置;CUDA event 确保拷贝完成后再刷新映射。
C3:策略无关的摊销成本。 不同驱逐策略(H2O、StreamingLLM、Random)必须复用同一套调度器、block 管理器和 worker 代码。解法:vToken 暴露三个接口——evict_token、sync_new_tokens、apply_moves——策略只需要调前两个,第三个是内部接口。策略适配器从 500+ 行代码降到 50 行以下。
数据说话:vToken 到底能省多少?
在 NVIDIA H100 80GB 上,用 Mistral-7B 和 Llama-3.1-8B,在 ShareGPT 和 LongBench 上测试三种驱逐策略(H2O、Scissorhands、Random):
内存效率
| 指标 | Naive-Evict | vToken | 提升 |
|---|---|---|---|
| 平均内存利用率(Llama-3.1-8B) | 基线 | +21.88% | — |
| 平均内存利用率(Mistral-7B) | 基线 | +21.67% | — |
| 保留 block 数 | 基线 | -27.2% ~ -72.3% | — |
关键洞察:Naive-Evict 的主要损失不是驱逐策略本身,而是 block 级运行时无法把 token 级存活信息转化为可回收的物理容量。
吞吐量
在相同 SLA 约束下(p95 延迟不超过 Naive-Evict 基线的 1.05 倍):
– Mistral-7B:吞吐 +9.9%~37.3%,p95 延迟 -9.9%~27.5% – Llama-3.1-8B:平均吞吐 +18.9%,p95 延迟 -14.7% – Scissorhands 策略下最强:吞吐 +33.3%~103.7%,p95 延迟 -21.8%~33.0%
Random 策略下 vToken 收益最大——因为随机驱逐让活 token 散落在各 block 中,Naive-Evict 的碎片化最严重。H2O 收益相对小,因为它的活 token 更集中。碎片越严重,vToken 越值钱。
容量前沿
这是最震撼的结果。在 gpu_mem_util=0.35(5427 个可用 KV block)下:
– Native vLLM 和 Naive-Evict:最多支持 C=5 并发 – vToken:支持到 C=8,并发提升 60%
在 gpu_mem_util=0.50(11519 个 block)下:
– Native vLLM 和 Naive-Evict:C=11 – vToken:C=22,并发翻倍
vToken 在 C=8 时吞吐 180.3 tokens/s,接近自己 C=5 时的峰值 203.2 tokens/s——优雅降级而非崩溃。
开销
– 纯间接开销(只装 vToken hook,不启用驱逐和回收):吞吐和 p95 变化 <1.0% – 主要开销来源:planner 端的机会检查(CPU 侧),不是 KV 搬迁 – 异步拷贝:无显式同步等待,拷贝在多个解码步内完成,GPU 拷贝时间远短于等待窗口
工程洞察:为什么这件事比看起来更重要
1. “颗粒度同构”原理的又一次验证
步子哥之前讨论过 Heddle 和 CodeRescue 的”颗粒度同构”——优化颗粒度应该和被优化对象的颗粒度一致。vToken 是这个原理的完美例证:
– 驱逐策略的颗粒度是 token – PagedAttention 的颗粒度是 block(16 token) – 颗粒度不匹配 → 块内碎片 40%-60% – vToken 加了一层 token 级虚拟化 → 颗粒度对齐 → 碎片消除
不是驱逐算法不够好,是运行时的颗粒度配不上驱逐算法的颗粒度。
2. “换层面解决问题”概念谱系的新成员
vToken 不是在 block 层面做优化(缩小 block、改分配策略),而是在 block 之上加了一层 token 级虚拟化。这和之前讨论过的”换层面解决问题”七部曲同构:
– 章鱼 RNA 编辑:不改 DNA,改施工图 – 黏菌外化记忆:不长神经元,用黏液 – 鸟类量子磁感应:不靠磁场强度,靠自由基对 – SOPHIA 分工:不同状态用不同出口方向 – EvoThink 原子推理:把推理流分段 – Möbius RoPE 拓扑干预:不调频率,调拓扑 – 螳螂虾声子盾牌:不硬抗,选择性过滤
vToken 是第八个:不缩小 block,加虚拟化层。 每次都是同一个模式——不在原来的层面死磕,换一个层面解决。
3. 操作系统设计模式的迁移
vToken 的设计几乎是操作系统虚拟内存的 KV 缓存版:
– Token Table = Page Table:逻辑到物理的映射 – evict_token = 标记页无效:只更新元数据,不立即回收 – 物理回收后端 = 页面回收 daemon:后台异步整理 – 异步 CUDA stream = DMA 传输:不阻塞主流程 – slot 映射刷新 = TLB 刷新:确保后续访问看到新布局
这不是巧合。操作系统的虚拟内存设计经过 60 年的打磨,已经证明了”逻辑地址空间和物理内存解耦”是管理稀缺内存资源的正确抽象。vToken 把这个抽象迁移到 KV 缓存管理,证明了同一个设计模式在新场景下的有效性。
4. 策略适配器从 500+ 行降到 50 行
这个数字值得单独拿出来说。在 vToken 之前,要把一个驱逐策略集成到 vLLM 里,需要改 4-6 个文件、500+ 行代码,因为策略逻辑和运行时逻辑紧耦合。vToken 把共享逻辑抽到运行时层,策略只需要实现 SelectVictims 接口。
这意味着什么?新驱逐策略的实验成本大幅降低。 研究者不需要懂 vLLM 的 block 管理细节,只需要决定”驱逐哪些 token”。这会加速驱逐策略的创新——就像操作系统的虚拟内存抽象让应用开发者不需要关心物理内存布局一样。
一个没被解决的问题
vToken 诚实地承认了它的局限:
– 单节点单 GPU:原型只针对单 GPU 解码快路径,分布式调度和跨设备 KV 搬移不在范围内 – 共享前缀块被保守跳过:为了避免破坏前缀缓存正确性,vToken 默认不回收共享前缀块。论文提到可以用 copy-on-write 解决,但当前实现没做 – planner 开销:主要 CPU 开销来自 planner 端的机会检查,而不是 KV 搬迁本身。这是工程优化目标
还有一个更深层的问题:vToken 目前是 vLLM 专属实现。 论文说抽象边界是通用的,但只有 vLLM 的实例化。TensorRT-LLM、SGLang 等其他推理引擎要享受同样的好处,需要各自实现 vToken 的 hook。论文没有提供开源代码(作者来自国防科技大学,可能有发布限制),这限制了社区的即时采用。
我的思考:虚拟化作为通用设计模式
vToken 让我想到一个更广泛的趋势:AI 系统正在重走计算机系统的路。
– PagedAttention = 物理内存管理(分页) – vToken = 虚拟内存(逻辑-物理解耦) – KV 驱逐策略 = 页面替换算法(LRU、LFU) – Prefix Cache = 页面共享(shared memory) – Chunked Prefill = 预取(prefetch)
每一个操作系统解决过的经典问题,在 LLM 推理系统中都在被重新解决一遍。这不是坏事——这意味着 AI 系统工程有大量成熟的计算机系统设计模式可以借鉴。vToken 的价值不只是”省了 21% 的 KV 内存”,而是证明了”虚拟化”这个 60 年前的设计模式在 LLM 推理时代依然有效。
下一个需要虚拟化的是什么?我赌是注意力计算的虚拟化——让 attention kernel 不直接访问物理 KV 位置,而是通过一层间接映射。这样稀疏注意力、滑动窗口、动态路由都可以在同一套运行时上实现,不需要每个策略都改 CUDA kernel。
vToken 告诉我们:当你发现两个层面的颗粒度不匹配时,不要在任何一个层面死磕——加一层虚拟化。 这个教训值得每个 AI 系统工程师记住。
—
论文:vToken: Token-Level Virtualization for Reclaimable KV Caches 作者:Yuanhang Gao, Xiangrui Yang, Yuanfeng Chen, Hongjia Chen, Qianru Lv, Wenfei Wu, Dongsheng Li 机构:国防科技大学、北京大学 基于 vLLM v0.18.0 实现,暂无公开代码仓库
