乐于分享
好东西不私藏

Tutti: KVCache卸载的瓶颈在控制路径

Tutti: KVCache卸载的瓶颈在控制路径

Tutti: Making SSD-Backed KV Cache Practical for Long-Context LLM Serving

问题

Tutti 解决的是 SSD-backed KV cache 在实际长上下文 serving 中太慢的问题。长上下文和高并发使 HBM 很快耗尽;DRAM 可扩容但成本高且容量仍有限;NVMe SSD 容量大、成本低,但现有 KV offload 路径延迟高。

论文的结论瓶颈不是 SSD 原始带宽,而是:

分页 KV 布局造成大量小随机 I/O:vLLM、SGLang 等系统的 paged KV memory 会把逻辑连续的 KV 分散成许多小块。
现有 GDS/LMCache 仍是 CPU-centric:即便使用 GPU Direct Storage,每个 I/O 仍需 CPU 发起或协调,CPU 位于 I/O control path。
CPU-GPU 同步和 DRAM staging 放大 GPU stall:论文报告现有 SSD tier 会导致 70% 到 80% GPU stall,甚至让复用 KV 慢于重算。

解决方案

Tutti 提出 GPU-centric SSD-backed KV cache object store,把 HBM 和 NVMe SSD 之间的数据路径与 I/O 控制路径从 CPU critical path 中移出。

主要设计:

GPU-native KV object abstraction:把 KV transfer 和 SSD I/O 粒度对齐,避免 page-level tiny random I/O。
GPU io_uring:在 GPU 侧模拟异步提交和完成队列,让 GPU 可以批量发起 object I/O。
SGL-based NVMe command path:用 scatter-gather list 降低 PRP 描述符开销。
Slack-aware I/O scheduling:按 layer profile 估算 compute slack,把 I/O 放进可隐藏窗口,避免 I/O kernel 与 compute kernel 竞争。
vLLM 集成:通过 KVConnector 与 vLLM 的 block-granular KV 管理衔接,论文实现约 8000 行 C++。

关键技术

GPU-centric object store

CPU 只负责异步加载每层 I/O kernel,不再为每个 block 发起 I/O。论文将 CPU 控制开销从O(layer * blocks)降到O(layer)

KV object abstraction

Tutti 引入 GPU file pool、NVMe file pool、P2P memory mapping table,使 GPU 可以以 KV object 为单位进行 bulk transfer。这个抽象是连接 LLM paged KV 和 SSD sequential bandwidth 的关键。

GPU io_uring

Tutti 将 I/O submit/completion 从 CPU 移到 GPU 侧,支持异步 GPU direct object I/O。GPU 可以在计算过程中并行推进 I/O 队列。

Slack-aware I/O scheduling

不同层 compute 时间不同,Tutti 通过 offline profiling 估计每层可隐藏 I/O 的 slack。优先调度读以降低 TTFT,写可以在 decode 中 best-effort flush。

数据

-端到端 TTFT 和 ITL

LEval 和 LooGLE,Llama3-8B,比较 HBM、LMCache-DRAM、LMCache-SSD、LMCache-GDS 和 Tutti。

  • 新版 vLLM 0.17.0 下,LEval 高负载时 Tutti 相比 DRAM 降低 TTFT 69.1%,相比 GDS 降低 78.3%。
  • 在 1s TTFT SLO 下,Tutti 的有效请求率相比 DRAM 提高 50%,相比 GDS 提高 100%。
  • LooGLE 中,0.6 RPS 时 GDS 的 TTFT 约为 Tutti 的 2.63x;Tutti 相比 DRAM 降低 93.2%,相比 GDS 降低 62.0%。
  • ITL 方面,LEval 1.5 RPS 下旧版 vLLM 中 Tutti 相比 DRAM 降低 60.4%,相比 GDS 降低 24.9%;新版中仍分别降低 22.0% 和 24.4%。

Retrieve/Store 原始带宽

  • Retrieve:LMCache-GDS 在双 SSD 下约 11.9 GB/s 饱和;Tutti 长上下文下最高 25.9 GB/s,最高 2.08x retrieve bandwidth。
  • Store:Tutti 在持久化存储 backend 中保持约 10 GB/s 写带宽,例如 128K tokens 下 9.8 GB/s;LMCache-GDS 约 7 GB/s。

PRP vs SGL

单 GPU thread 读写 500 MB:

路径ReadWritePRP0.287 GB/s0.032 GB/sSGL8.891 GB/s2.922 GB/s

SGL 相比 PRP:读提升 31.0x,写提升 91.3x。这个 microbenchmark 说明 I/O command path 对 SSD KV cache 很关键。

不同 prefix length 的 TTFT

固定总输入 128K,cached prefix 从 16K 到 128K:

  • 112K prefix 时 LMCache-SSD TTFT 为 7.84s,Tutti 为 3.43s,快 2.28x。
  • 相比 LMCache-GDS,Tutti 在所有 prefix length 都更优,提升范围 5.8% 到 61.4%。
  • 16K 到 96K 中等复用区间,Tutti 甚至可超过 DRAM,最高 13.4% improvement;极高复用时 DRAM 因纯内存读恢复优势,Tutti 最多落后 20.6%。

多 GPU 长上下文扩展

GLM-4-9B-Chat-1M,2 GPU、4 disk:

  • 128K prefix 下,Tutti TTFT 为 155.743s,LMCache-GDS 为 207.12s,约 25% latency reduction。
  • LMCache-GDS 在 512K 和 640K 下因 staging buffer 导致 OOM,Tutti 可完成测试。
  • 结论:GDS 作为外部插件式 I/O 加速在极长上下文下受 staging memory 约束,而 Tutti 的深度集成避免了额外 staging buffer。

bubble time 分解

固定 prompt length 32K,改变 cache hit rate:

  • Tutti 大多数区间 bubble time 可忽略,平均约 25 ms。
  • 93.75% hit rate 下 bubble time 仅约 6 ms。
  • Tutti 将 compute-bound 到 I/O-bound 的 crossover point 推到 98.3% hit rate。
  • 结论:layerwise async pipeline 基本把 SSD transfer 隐藏进 compute slack。

每 100 万 token 成本

成本模型使用 $5/hour H100、$0.0088/GB/hour DRAM、$0.000082/GB/hour NVMe SSD。

  • SSD 每 GB 成本约比 DRAM 低 100x。
  • LooGLE 0.5 QPS 下,Tutti 相比 LMCache-SSD 成本降低 66.2%,相比 LMCache-GDS 低约 27%。
  • 论文总结中给出总体服务成本降低约 27%。

工程价值

Tutti 的核心价值是证明 SSD KV cache 的问题不一定是 SSD 慢,而是现有控制路径和 transfer granularity 不适合 LLM paged KV。对于希望把 prefix cache 容量从 DRAM 扩到本地 NVMe 的 serving 系统,Tutti 是近期最重要的系统论文之一。

局限

  1. 强依赖硬件和系统栈:NVMe 拓扑、GPU direct I/O、驱动、kernel 调度、vLLM 接口都会影响效果。
  2. 主要面向本地 NVMe SSD,不等价于远程 SSD pool 或对象存储。
  3. 解决的是 KV restore/store path,不解决语义 cache matching、approximate reuse 或 non-prefix reuse。