大模型推理引擎 KV Cache 三级存储架构:从 GPU HBM 到 CPU DRAM 再到 NVMe SSD 的工程实践

引言:当 KV Cache 成为推理成本的核心矛盾

2026 年,大模型推理成本的结构正在发生深刻变化。随着 MoE 架构的普及(DeepSeek-V3、Qwen3 等仅激活部分参数),计算成本持续下降,但显存中 KV Cache 的占用却在剧增。以 DeepSeek-V3 671B 为例(激活 37B,128 个稀疏 KV Head),长上下文 2048K 推理时,单层 KV Cache 就可能超过 120MB,128 层累积可达 GB 级别。与此同时,H2O(纯 GPU HBM)方案的最大容量被物理硬件死死限制。

KV Cache 三级存储架构(GPU HBM → CPU DRAM → NVMe SSD)正是为打破这一瓶颈而生。它通过在不同存储层级之间动态调度 KV Cache 页面,实现了 "用容量换成本、用延迟换吞吐" 的工程折中。

本文将从底层原理到生产级实现,深度拆解 KV Cache 三级存储架构的完整设计:Packed Page Table、L3 预取决策、换页优先级、NIXL/Mooncake 远程卸载、以及与硬件卸载引擎的协同。


一、KV Cache 的内部结构:理解每一字节

要理解三级存储,先要理解 KV Cache 本身的物理含义。对于 Transformer 模型,每个 token 在每个 attention 层都会产生 K 和 V 向量:


K[t] = W_k · x[t]    # shape: [num_kv_heads, head_dim]
V[t] = W_v · x[t]    # shape: [num_kv_heads, head_dim]

以 DeepSeek-V3(MLA 架构)为例:

参数 值
num_kv_heads (压缩) 128
head_dim (latent) 512
pagesize (vLLM 默认) 16 tokens/page
数据类型 fp16/bf16

每页 KV Cache 大小 ≈ num_kv_heads × head_dim × pagesize × 2(K+V) × dtype_size,即 128 × 512 × 16 × 2 × 2 = 4,194,304 字节 ≈ 4 MB/层/page。

这意味着:一层一个 token 的 KV 仅约 256 KB,但 128 层 32K token 上下文的 KV Cache 就可能突破 50 GB。


二、基础支撑:PagedAttention 与虚拟页表

三级存储建立在一个前提之上——KV Cache 必须以页为单位管理和寻址。这就是 vLLM 提出 PagedAttention 的核心贡献。

2.1 PagedAttention 内存模型

传统方式为每个 request 预留连续最大长度显存(浪费 60%+)。PagedAttention 将 KV Cache 拆成固定大小的逻辑页,通过 Block Table(页表)映射逻辑块号到物理页框:


class PagedAttentionManager:
    def __init__(self, page_size=16, gpu_page_count=1024, cpu_page_count=4096):
        self.page_size = page_size  # tokens per page
        self.gpu_allocator = PageAllocator("GPU", gpu_page_count)
        self.cpu_allocator = PageAllocator("CPU", cpu_page_count)
        # Block Table: seq_group -> layer -> logical_block_id -> physical_page_id
        self.block_table: Dict[str, Dict[int, List[int]]] = {}

    def allocate_request(self, request_id: str, prompt_len: int) -> BlockTable:
        pages_needed = ceil(prompt_len / self.page_size)
        pages = self.gpu_allocator.allocate(pages_needed)
        self.block_table[request_id] = pages
        return pages

2.2 缺页异常(Page Fault)与换页触发

当 GPU KV 内存不足以支撑新的 prefill 或 decode 步骤时,触发 L2(CPU 内存)调度:

  • L1_evict → L2:将 GPU 上的冷页驱逐到 CPU DRAM
  • L2_fetch → L1:解命中 CPU 上的页需要被访问时提回 GPU
  • L3(NVMe):当 CPU DRAM 也溢出时,进入 L3 NVMe 卸载

触发条件:
  GPU allocated_pages / GPU total_pages > 80%  →  evict 冷页到 CPU
  CPU allocated_pages / CPU total_pages > 70%  →  evict 至 NVMe (可选)
  next_attention_page not in GPU page_table →  page fault → fetch from L2/L3

三级存储调度策略

3.1 访问热度与空闲空闲 LRU-K

三级调度的核心在于准确区分 "热 KV 页" 和 "冷 KV 页"。最常用的启发式方法是 LRU(Least Recently Used) 及其变体。

在多请求并发场景下,LRU-K(跟踪每个页最近 K 次访问间隔)能更准确地识别 "即将被注意力扫描" 的页面:


class LRUKScheduler:
    def __init__(self, k=2):
        self.k = k
        self.access_history: Dict[int, List[float]] = {}  # page_id -> access timestamps

    def record_access(self, page_id: int, ts: float):
        if page_id not in self.access_history:
            self.access_history[page_id] = []
        self.access_history[page_id].append(ts)
        # 仅保留最近 K 次
        if len(self.access_history[page_id]) > self.k:
            self.access_history[page_id] = self.access_history[page_id][-self.k:]

    def predict_soon_use(self, page_id: int) -> bool:
        """基于历史间隔预测近期是否会再被访问"""
        history = self.access_history.get(page_id, [])
        if len(history) < self.k:
            return False
        # 访问间隔稳定且小于阈值 → 预测持续活跃
        interval = history[-1] - history[-2]
        return interval < 100  # ms 阈值

3.2 预加载与分批 CPU→GPU 拷贝

GPU → CPU 的拷贝延迟大约在 20-40 GB/s(PCIe Gen4 x16),而 NVMe → CPU 在 7-14 GB/s(PCIe Gen4 NVMe)。为了掩盖这些延迟,生产者线程通常会采用异步流水线:


# 简化的三级调度伪代码
async def triple_level_kv_manager():
    gpu_free = gpu_allocator.free_pages
    while running:
        # 1. 预取:从 CPU 向 GPU 按需取页
        pending_fetch = prefetch_queue.drain(limit=8)
        for page, dest_gpu_addr in pending_fetch:
            await dma_engine.copy_h2d(page.cpu_buffer, dest_gpu_addr)
            gpu_allocator.register(page.id, dest_gpu_addr)

        # 2. 内存压力检查
        if gpu_allocator.utilization() > 0.85:
            # 识别冷页(LRU 尾部)
            cold_pages = lru_queue.get_tail_n(16)
            for pg in cold_pages:
                cpu_slot = cpu_allocator.allocate(1)
                if cpu_slot:
                    await dma_engine.copy_d2h(pg.gpu_buffer, cpu_slot.buffer)
                    gpu_allocator.free(pg.id)
                else:
                    # CPU 也满了 → 尝试 NIXL 远程卸载
                    nixl.offload(pg, remote_node="cpu-pool-1")

        # 3. Swap Scheduler
        await asyncio.sleep(0.001)

3.3 批处理调度与 KV Cache 重用的耦合

在实际调度器中,三级存储还需要与 Continuous Batching 和 Prefix Caching(Prompt Cache) 深度耦合:

  • Prefix Cache:相同前缀的 prompt 可直接复用已计算好的 KV 页,仅在 GPU 命中时使用前缀。如果前缀 KV 已被驱逐到 CPU,则复用链路为 CPU→GPU→Attention,相比纯 GPU miss 仍快 10× 以上。
  • Chunked Prefill:将长 prefill 拆为小 chunk,与 decode 三级交织调度,期间 KV 页可在不同层级间流动。

四、远程 CPU 卸载:Mooncake 与 NIXL 的工程实践

三级存储的极致形态是将 "CPU DRAM" 从本地延伸到远程 CPU 内存池(Disaggregated Memory)。这是 Mooncake(LMSYS 团队)与 NIXL(NVIDIA)的核心设计:

4.1 NIXL 的 KV Cache 卸载原理


┌──────────────────────────────────────────────────────┐
│  KV Cache 卸载目标:                                  │
│  当单机 GPU 内存不足时,                              │
│  通过 RDMA/PCIe 将 KV 页远端放置到:                    │
│    - 本地 CPU DRAM(L2)                             │
│    - 远程 CPU 内存池(L2')                          │
│    - NVMe KV Store(L3)                             │
│    - 远程 GPU Pool(L1')                            │
└──────────────────────────────────────────────────────┘

NIXL 执行器的工作流:

  1. 预热(Prefetch):在请求到达前将对应 KV 预发到 GPU
  2. 解耦执行(Compute-Transfer Overlap):将 KV 上传与 GPU 计算在时间上重叠
  3. 属性感知调度(Affinity-Aware):KV 访问遵循 "热数据近 GPU、冷数据远 SSD" 的亲和力
  4. Benchmark Attention:三星 HBM4 2026 年在推理推理性能表现如下(8×H200 SXM5)

    场景 显存限制(HBM only) 三级存储扩容 TTFT 延迟增长 吞吐提升
    70B 中文 32K 连续 32 请求 11.2 req/s (HBM 141GB) 3× CPU DRAM +4.2 ms +31%
    70B 中文 128K 并发 128 请求 18.6 req/s (HBM only) 8× CPU DRAM +12.8 ms +115%
    70B 混合任务 32+128K 持续 2h 9.4 req/s 动态 KV 池 +2.1 ms +53%
    100B+ MoE EP32 真实流量 Out of Memory NVMe 溢出 +45 ms 稳定运行

    注:以上为理论峰值与实验室对比数据,实际性能受请求混合度、序列长度分布、Triton Kernel 优化等影响。


    五、2026 年生产级方案对比

    截至 2026 年底,这一领域的生产级方案呈现出四足鼎立:

    5.1 vLLM v1 + Mooncake Disaggregated Prefill

    vLLM 0.7+ 版本已支持通过 Connector 协议集成 Mooncake,实现 Prefill-Decode 分离部署(P/D Disaggregation):

    • Prefill 节点生产 KV → 通过 Mooncake Connector 发送 → Decode 节点消费 KV
    • KV Transfer 内核层做 RDMA 直接内存写入
    • Prefill 节点的 KV 不再占用 GPU 内存,直接卸载到 CPU 节点

    5.2 SGLang + DeepEP 集成

    SGLang 0.4 后引入的 RadixAttention(Radix Tree)进一步将前缀复用从单 node 扩展到多机:

    • 通过 Radix Tree 支持更丰富的 KV Cache 共享模式(共享前缀、beam search 副本)
    • Mooncake Transfer Engine 后台预取 KV Cache 到 GPU

    5.3 Dynamo(NVIDIA 新方向)

    NVIDIA Dynamo 框架更深入地使用了 NIXL 来执行自动 KV Cache 路由:

    • SGLang/vLLM 后端共享同一 KV Cache 存储后端
    • 支持 "分形 KV Cache"(Hierarchical KV Cache):GPU → CPU → Remote CPU → NVMe

    5.4 方案落地的关键指标

    决策维度 GPU HBM Only DRAM Swapping 三级存储推荐
    延迟敏感度 < 10ms TTFT 20-50ms TTFT 15-30ms TTFT
    吞吐撑大倍数 1× 1.3-1.8× 1.5-3.5×
    最大并发数 显存硬上限 DRAM 上限 CPU 内存池
    成本/Token 极高 中 低
    实现复杂度 简单 Kernel 编码 需要网卡/CS

    六、工程优化实战:从理论到稳定输出

    6.1 页大小选择的艺术

    在三级存储中,页大小直接影响 DMA 传输效率与淘汰精度:

    • 4 tokens/page:细粒度,适合 max_seq_len < 8K 的场景;但 page table overhead 大
    • 16 tokens/page(vLLM 默认):适应性好,但是批量拷贝时效率损失大
    • 64 tokens/page:高页大小,适合长上下文场景(≥128K),CUDA transfer 效率高;但细粒度预测的页命中率下降

    生产建议:在线上部署中,通常采用自适应 page size:

    模型上下文 推荐 page size
    ≤ 8K 8-16 tokens
    32K-128K 16-32 tokens
    ≥ 128K 32-64 tokens

    6.2 Prefetch 预测模型

    根据未来 4-8 个 token 生成的注意力访问模式,构建一个轻量级 Prefetch Strategist 模块,预测下一批将需要的 KV 页:

    
    class PrefetchPredictor:
        def predict_next_pages(self, active_seqs: List[Sequence]) -> List[int]:
            """基于当前 batch 的注意力访问模式,预测即将被访问的 KV 页"""
            candidate_freq = defaultdict(int)
            for seq in active_seqs:
                # 本轮已经访问的最后一个 page
                last_page = seq.current_page
                # 后续页预测(按 chunked prefill 步长步进)
                chunk_step = 256 if seq.is_prefill else 2  # decode 每次 1-2 页
                for lookahead in range(1, 8):
                    next_page = last_page + lookahead * chunk_step
                    candidate_freq[next_page] += (8 - lookahead)  # 距离越近、权重越高
    
            # Top-K 预测页
            topk = heapq.nlargest(4, candidate_freq.items(), key=lambda x: x[1])
            return [p for p, _ in topk]
    

    6.3 NUMA 拓扑感知的 CPU 页放置

    在多路 CPU 系统中(如 2× Intel Xeon 4th Gen Sapphire Rapids 或 AMD EPYC 9004),NUMA 节点亲和性对 KV Cache 的 DMA 性能影响巨大:

    
    错误配置:CPU 页在 Node 0,GPU 连接在 Node 1
      → QPI/UPI 跨越延迟 ~100ns,DMA 吞吐量下降 40%
    
    正确配置:使用 nvidia-peermem 自动绑定 NUMA 拓扑
      → GPU 与最近 NUMA 节点对齐,DMA 带宽提升 3.1×
    

    生产环境启用:

    
    # 检查 GPU NUMA 节点
    nvidia-smi topo -m
    # 绑定 CPU 分配池到最近的 NUMA 节点
    numactl --membind=0 --cpunodebind=0 python -m vllm.entrypoints.openai.api_server ...
    

    七、深度 QKV Cache 投机缓存(Speculative KV Reuse)

    三级存储与投机解码(Speculative Decoding)结合时,最棘手的问题是:被拒绝的投机 token 产生的 KV 也需要被清理或回滚。完整的解决方案是 Multi-Version Page Table(MVCC 思想):

    • 每个投机步骤前,记录当前 KV 页表的快照( Copy-on-Write page table)
    • 验证投机 token 时合并正确的 KV,回滚错误分支(仅修改页表引用元数据)
    • 这一步在三级存储版本中具有额外复杂度——被回滚的页可能在 CPU 上的缓存也需要同步版本标记
    
    class KVRollbackHandler:
        def speculative_push(self, page_table: BlockTable, token_id: int) -> VersionId:
            """为投机 push 前的 KV 页打上快照版本"""
            version = VersionId(self.snapshot_counter.increment())
            page_table.mark_snapshot(version, token_id)
            return version
    
        def rollback(self, page_table: BlockTable, version: VersionId):
            """版本标记回滚 → 关联 L2/L3 缓存也回到上一版本"""
            page_table.restore_snapshot(version)
            # 通知 CPU 内存池清理对应快照
            cpu_pool.evict_snapshot(version)
    

    八、未来展望:从三级到 KV-Aware 架构系统化

    KV Cache 三级存储正在推动推理基础设施迈向 KV-Aware 系统化架构:

    时间线 技术方向 影响
    2026 年末 NIXL 开源成熟 KV 异构卸载标准化
    2027 年上半年 HBM4 商用 + CXL 3.0 内存池 四级存储升级为 CXL 内存池(L2.5)
    2027 年下半年 GPU 原生 KV Direct Access(NVIDIA Rubin) GPU 可直接 DMA 访问 NVMe,省去 CPU 拷贝
    2028 年 KV Cache 联邦学习(Sandbox KV) KV 加密跨节点传输,实现隐私计算推理

    其中最具革命性的方向是 GPU 原生 KV Direct Attention(或 NVIDIA 所称的 "KV Cache Virtualisation" 支持),即 GPU 内核可直接对分布在不同层级存储中的 KV 执行 attention 计算而不影响带宽——这将从硬件层面实现真正的 "存储级注意力"。


    总结

    KV Cache 三级存储架构(GPU HBM → CPU DRAM → NVMe SSD)已经从 2025 年的前沿研究落地为 2026 年生产级推理基础设施的核心组件。其核心设计逻辑可归纳为:

    1. PagedAttention 虚拟化:以固定页为调度单位,消除连续分配碎片
    2. LRU-K 热度预测:区分冷 KV 与热 KV,决定页驻留层
    3. RDMA 异步预取:用生产者-消费者模型掩盖传输延迟
    4. Multi-Version Rollback:与投机快照深度协同拒绝后的快速回滚
    5. NUMA 亲和 + Adaptive Page Size:最大化 DMA 带宽效率
    6. 在大模型推理降本的大背景下,三级 KV 存储架构不仅是工程优化,更是将 "HBM 稀缺性" 转化为 "可调度性" 的关键基础设施。在 HBM4 大规模部署与 CXL 3.0 普及的 2027 年,一个完整透明、跨节点、存储层次感知的 KV Cache 调度系统,终将成为推理平台的默认底座。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部