· 陈宸,仇实 · technical · 16 min read
LMCache&xPU.IO:当LMCache遇上直通存储
随着Agent成为主流,推理要处理的上下文已经增长到1M级别。Phoenix为LMCache提供了一个高性能直通存储后端,在生产trace回放中把高并发下的平均TTFT压低了5.6×,推理系统QPS提升2x以上。
概述
随着Agent成为主流,推理要处理的上下文已经增长到1M级别,单条会话的KV Cache涨到几十GB。HBM和DRAM的容量有限,价格还在一路上涨。
把KV Cache卸载到NVMe SSD是更划算的选择:单机就能有100TB级容量,成本也低得多。但SSD是离GPU最远的一层,数据取回来的延迟一高,GPU只能空等,利用率随之下降。
GPU直通存储让存储设备直接向GPU显存传输数据,主机内存不参与数据搬运。
Phoenix是一个全栈开源直通存储I/O栈,支持POSIX接口,使用io_uring进行加速,且跨厂商,支持多种存储后端。Phoenix为LMCache提供了一个高性能直通存储后端,在生产trace回放中把高并发下的平均TTFT压低了5.6×,推理系统QPS提升2x以上。
💡 关于 xPU.IO
xPU.io由腾讯DCPD(数据计算平台部)发起,专注面向AI应用的xPU直通存储,包括Phoenix以及Tutti(GPU-centric KV cache存储)。
KV Cache 的容量与速度挑战
负载特征变化
当前KV Cache的负载已经从chatbot转向agent,agentic负载特征是长horizon的多轮loop,在推理和工具调用之间反复切换。
Codex在SWE-bench Pro上的真实trace数据:
- 📊 到第30轮时上下文通常已增长到约 80K tokens
- 📈 最长超过 180K tokens
- 🔄 每轮真正新增的只有几百到几千token
- 💾 Input/Output token比约为 131:1
- ✅ 缓存命中率 94.2%
前缀缓存住,prefill在这部分几乎是免费的,每轮成本只剩增量输入的计算成本。
两个核心要求
📦 容量要求
100K token的上下文动辄几十GB级,一台实例上挂着大量长会话,HBM和DRAM很快就会触发淘汰,而淘汰意味着下次回来要从零重算整段前缀。
SSD是唯一能承载这个量级的介质,单机100TB级,长会话数据可长期留存。
⚡ 速度要求
Trace里轮与轮之间的间隔中位数只有**几秒**,这就是留给加载的全部窗口:几十GB级的KV要在这几秒内回到显存。
缓存能否带来收益,取决于取回一段KV的耗时是否显著低于重算它;加载太慢,命中率再高也换不来时间。
现有路径:KV 需要经主机内存中转
问题分析
现有LMCache的文件存储方案,将SSD当作DRAM缓存,这种分层结构的特性恰好和需求相反:
容量最大的那层,离GPU最远、带宽最窄
数据两次穿越PCIe,中间多一次拷贝,整个过程还要CPU全程参与发起、同步、回调。
buffer = malloc(len); // 主机内存做跳板
cudaMalloc(&devPtr, len);
pread(fd, buffer, len, offset); // 盘 → 主机内存
cudaMemcpy(devPtr, buffer, len, H2D); // 主机内存 → 显存性能损失
实测数据(4块PCIe 4.0 NVMe RAID 0):
| 指标 | 数值 | 说明 |
|---|---|---|
| 🔷 底层NVMe带宽 | 25 GB/s | iostat观测 |
| 🔶 端到端KV加载 | 10.2 GB/s | 实测性能 |
| 📉 带宽损失 | <40% | 不到裸盘的四成 |
Codex SWE-bench回放测试(20,230条请求):
- 平均prompt长度:~68K tokens
- TTFT均值:1101 ms
- SSD加载时间:~600 ms(蓝色部分)
- GPU prefill:313 ms
- 主机内存中转导致了额外延迟
Phoenix:去掉伪缓冲区的直通存储栈
核心理念
Phoenix是一个开源的GPU直通存储中间件,最初发表在SC’25上。
它解决的核心问题是:传统上存储设备(如NVMe SSD)里的数据要先经过CPU和内存、再复制进GPU显存,路径长、开销大。
Phoenix让存储数据”绕过”CPU,一步直达GPU显存,从而显著提升数据加载速度、释放CPU算力。
技术优势
构建在操作系统标准I/O栈之上,不依赖私有驱动
无需修改应用和文件系统的使用方式
不需要umount/mount等操作,通过io_uring/libaio充分利用NVMe带宽
已在NVIDIA、沐曦等厂商上验证
LMCache 集成 Phoenix
GDS L1框架
LMCache原生支持直通存储,提供了一套开放的GDS L1框架。它将NVMe上的slab file作为L1存储,对标CPU pinned DRAM以及Device-DAX。
GDS L1框架自顶向下可以分为两层:
🔷 DMA驱动层
- ✦ 核心:进程全局的
GDSContext - ✦ 驱动GPU和slab file之间的DMA
- ✦ 维护小型staging buffer
- ✦ 通过stream保证操作有序性
🔷 后端抽象层
- ✦ 提供统一的GDS Backend抽象
- ✦ 允许不同直通存储方案接入
- ✦ GDSContext对具体Backend无感知
- ✦ 只与标准API打交道
Stream Ordered I/O
Phoenix专门实现了对Stream Ordered I/O的支持。基于加速器runtime的LaunchHostFunc原语,Phoenix把一段由Host发起的DMA read/write包装成一个host callback挂到调用方的stream上。
顺序语义保证:
- 📍 一次DMA Read之后排的kernel,一定看得到DMA搬回来的数据
- 📍 一次DMA Write之前排的gather kernel,一定已经跑完,DMA读到的是稳定的数据
- 📍 不需要额外的同步原语,同一条流上连续提交的DMA按提交顺序执行
集成方式
受益于GDS L1的开放性,向LMCache集成Phoenix的过程非常自然且轻量。
Phoenix项目为LMCache专门实现了一个API冻结的用户态库libphxfile,并基于这一用户态库实现了后端抽象层所需的接口,GDSContext就能够顺畅地通过Phoenix来直接进行GPU和NVMe之间的DMA了。
Phoenix Backend架构
层级结构
应用与框架
vLLM / LMCache / 自定义应用
↓
应用适配器
phxloader / phxcache / phxfile
↓
用户态 libphoenix
设备发现、显存注册、地址解析
同步/批量/异步 I/O ↔ phoenixfs
↓
Linux 文件系统与块层
pread/pwrite 或 io_uring
→ NVMe/RDMA DMA关键特性
🔌 Adapter 层
对接AI应用,Phoenix通过Adapter把能力交给应用:
- ▸ libphxfile:纯C库,面向LMCache KV Cache卸载
- ▸ phxloader:pybind11扩展,面向vLLM模型权重加载
Adapter这一层同时是解耦点:无论Phoenix内部怎么演进,应用侧都不感知
📂 I/O 层
复用Linux原生I/O栈:
- ✓ GPU显存变成Linux I/O可直达的内存
- ✓ 直接使用pread/pwrite、io_uring、线程池
- ✓ 基于io_uring的批量I/O接口,一次系统调用下发N个请求
- ✓ 4块PCIe 4.0 NVMe在512并发下能完全吃满硬件带宽
🔧 内核层
让显存成为内核认识的内存:
- ◆ 借助Linux 4.3引入的ZONE_DEVICE机制
- ◆ 为GPU显存建立struct page
- ◆ 显存指针成为合法地址,不需要伪缓冲区
- ◆ I/O直接走标准的文件系统、块层和原生NVMe驱动
🌐 兼容性
存储与GPU之间的万能Hub:
- ★ 内核侧抽象显存的P2P映射
- ★ 用户态侧抽象设备查找与显存拷贝原语
- ★ 接入新加速卡只需补两个实现文件
- ★ 支持ext4/xfs/NFS/BeeGFS/Lustre等文件系统
性能结果
测试配置
测试环境:
- 🖥️ 硬件:8×H20(每卡96GB HBM)+ 4块NVMe Gen4 RAID 0
- 🤖 模型:TP=8,256K上下文
- 📊 Trace:生产trace(11,596条请求)
- 👥 用户:请求次数6~476,平均长度70K,最大256K
- 💾 KV Cache容量:约3.5TB
三种配置对比
🟠 DRAM Only
1TB DRAM
LRU淘汰策略
Cache miss重算
🔵 SSD中转
25TB SSD + 400GB DRAM
经主机内存中转
需要CPU参与
🔴 Phoenix
25TB SSD
GPU直通存储
无需主机内存
关键指标
🎯 性能提升
服务能力对比(TTFT 5秒红线):
- 🟠 DRAM Only: 7并发
- 🔵 SSD中转: 13并发
- 🔴 Phoenix: 37并发
性能分析
为什么Phoenix更快?
- 省掉主机内存中转 → 单次加载耗时直接降低
- 同样时间喂进GPU的KV更多 → 吞吐量提升
- 调度优化重新生效 → KV加载和推理可以overlap
- 加载快于计算 → 气泡被压进计算间隙
- 并发越高,曲线越平 → 规模化优势明显
LMCache+Phoenix 使用方法
快速开始(三步启动)
# ① 构建并安装 Phoenix(内核模块 + libphoenix)
git clone https://github.com/xPU-IO/Phoenix.git && cd Phoenix
mkdir -p build && cd build && cmake .. && make -j && sudo make install
nvidia-smi # 先确保厂商驱动已加载
sudo make insmod # 加载 phoenixfs 内核模块
# ② 构建并安装 LMCache 适配层(libphxfile.so)
bash adapters/LMCache/phxfile/install.sh
# ③ 启动 LMCache,指定后端
LMCache server ... \
--gds-l1-backend phx \
--gds-l1-path /mnt/nvme/LMCache_gds说明:
--gds-l1-path指向任意一个可写目录,slab就是那里的一个普通文件- LMCache侧只与
libphxfile.so交互,libphoenix对它透明 phxFile*这套ABI已冻结,Phoenix的内部演进均在shim层吸收- 当前Phoenix已经支持5.0+的Linux内核
结语
💫 总结
📦 KV Cache需要更大的容量,SSD是当下唯一划算的选择
⚡ 容量解决了,速度还没有:数据从盘里取回显存要先在主机内存过一道,带宽打折、CPU陪跑
🔧 LMCache提供了开放、可灵活扩展的KV Cache管理框架,让不同存储后端能够方便地接入推理系统
🚀 Phoenix把这段路拉直,让存储直接向显存写数据。加载快了,L1就不必再靠昂贵的DRAM撑着
💰 用SSD做L1:容量更大、成本更低,同样的硬件和延迟要求下还能接更多并发
参考链接
🎉
感谢阅读!
欢迎加入 xPU.IO 社区,共同探索 AI 基础设施的未来