vAttention
Contents
概览
论文通过 CUDA 提供的虚存管理 API,解决了在用户态管理显存分页的问题。对比 FlashAttention 和 FlashInfer 中的 PagedAttention kernel,分别获得了 22%、29% 的性能提升。
论文地址:vAttention: Dynamic Memory Management for Serving LLMs without PagedAttention
动机
vLLM 通过引入 PagedAttenion,通过分页管理显存,来节省 KV Cache。但是这带来了几个明显的问题:
- 需要重写 GPU kernel,难以复用现有代码
- CPU 侧推理框架代码复杂,难以维护
- 性能开销
- GPU 侧:在 FlashAttention、FlashInfer、TensroRT-LLM 等框架中,PagedAttention 的性能比不分页的 Attention kernel 差 10%~28% 不等
- CPU 侧:管理页表也带来了明显的开销
方法
基本观察
对推理框架有这样两个观察:
- KV Cache 需求可预测:在 decode 单个 token 的粒度,每轮迭代所需要的显存是可预测的
- KV Cache 不需要高内存分配带宽:单个 token 的 KV Cache 大小并不大,因此 KV Cache 的分配需求(以 MB/s 计)受限于解码速度,处于一个较低的水平 基于这两点观察,高效的动态 KV Cache 分配机制成为可能。
系统设计
下文用到的 CUDA 虚存 API:https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__VA.html
- 系统启动阶段,预分配所有显存
- 虚存:按模型所需要的最大 context length 分配,分配的虚存可以远远大于实际的显存大小
- 物理显存:按 GPU 能分给 KV Cache 的最大量分配
- 推理阶段,动态映射虚存和物理显存
- 在每次 decode a token 之前将虚存映射到物理显存
- 解码结束后取消虚存和物理显存的映射
- 系统关闭阶段,释放所有的虚存和物理显存
优化
- CUDA 默认的 page 是 2MB 大,可以通过扩展 GPU 驱动的方式,构造一些能分配 64KB、256KB 大小的页面
- 我个人认为这个方式是不可维护的
- 显存分配和计算重叠。第 轮 decode 计算时,可以并行分配第 轮的显存
- 显存回收推迟。某个 decode 请求完全结束后,不立刻取消虚存和物理显存的映射,而只是清零显存,分配给下一个新的请求。这样只有在下一个新的请求 decode 长度大于本次请求长度时,才需要额外的虚存-物理显存映射
- 反过来,如果下一次请求的 decode 长度更短,会造成显存浪费。因此设定系统的可用显存降低到某个阈值以下后,再回收物理显存