推理与 KV Cache 04:如何判断 KV 卸载是否划算

作者:CherryYang 发布时间: 2026-11-12 阅读量:5 评论数:0

判断 KV 卸载是否值得,需要同时核对容量预算、有效传输带宽和数据使用截止时间,再比较恢复缓存与重算的总耗时。本文用教学算例解释这些约束,并区分前缀恢复、活跃 KV 加载和 PCS 的统计口径。除明确标注 KiB、GiB 的地方外,GB 与 GB/s 均使用十进制。


两种推理负载都需要 1000 GB/s 的 KV 读取带宽。A 需要同时保留 30 GB KV,B 需要 200 GB。假设设备扣除权重等开销后,能给 KV 留出 80 GB,并在相同访问模式下持续提供 2000 GB/s。

A 的两项需求都在预算内;B 在讨论读取速度之前就已经装不下。把部分 KV 放到外层存储,能够缓解容量问题,却同时引入数据恢复与等待。

先对齐容量与带宽的口径

容量需求 CwC_w 表示目标并发下需要同时保留多少字节;带宽需求 BwB_w 表示为达到目标吞吐,在指定路径上每秒传输多少字节。一份 KV 可以只占一份空间,却在连续 Decode 中被反复读取。

将需求分别除以可用预算 CdC_d 与 BdB_d:

uC=CwCd,uB=BwBdu_C=\frac{C_w}{C_d},\qquad u_B=\frac{B_w}{B_d}

图 1 容量与带宽的预算占比
同一刻度为 0–250%,每条轨道的竖线标在 100%。

负载 A

容量 30 / 80 GB · 37.5%

带宽 1000 / 2000 GB/s · 50%

负载 B

容量 200 / 80 GB · 250%,超出预算

带宽 1000 / 2000 GB/s · 50%

A 的容量占比为 37.5%,带宽占比为 50%。若两项需求按相同比例增长,带宽先碰线。当前占比不代表真实程序已经受带宽限制,还需观察计算、调度和其他访问。

B 的容量占比为 250%。如果整个工作集都要求驻留在这 80 GB 中,就缺少 120 GB;提升读取带宽无法补上空间缺口。

容量带宽比只保留了相对压力

容量除以带宽的单位是时间。设备侧 Rd=Cd/BdR_d=C_d/B_d 表示以该有效速率搬完一份容量需要多久;负载侧 Rw=Cw/BwR_w=C_w/B_w 则表示按目标访问速率搬运一份工作集的时间尺度。

RwRd=Cw/BwCd/Bd=uCuB\frac{R_w}{R_d}=\frac{C_w/B_w}{C_d/B_d}=\frac{u_C}{u_B}

本例中设备的时间尺度为 0.04 s,A 为 0.03 s,B 为 0.2 s。它们分别对应 A 的带宽余量更小、B 的容量余量更小。

但把 A 的两项需求都乘以 3,RwR_w 仍为 0.03 s,实际需求却变成 90 GB 与 3000 GB/s,两项都超预算。比值适合比较压力方向,能否承接负载仍需核对绝对需求。

平均带宽够,不代表一次恢复赶得上

假设恢复路径每秒处理 10 次取回,每次搬 100 MB,链路提供 20 GB/s 的有效传输速率。平均带宽只需 1 GB/s,但一次纯传输约需 5 ms。

图 2 取回同一批 KV 时,要同时看两笔账

平均带宽需求

10 次/秒 × 100 MB/次 → 1 GB/s

单次传输耗时

100 MB ÷ 20 GB/s → 约 5 ms,按恒定速率估算

对单次恢复,可先用下面的分解审查关键路径:

Trestore≈Tqueue+Tsetup+DtransferBeffective+Tconvert+TsyncT_{\mathrm{restore}}\approx T_{\mathrm{queue}}+T_{\mathrm{setup}} +\frac{D_{\mathrm{transfer}}}{B_{\mathrm{effective}}} +T_{\mathrm{convert}}+T_{\mathrm{sync}}

这些项在实现中可能重叠,应以实际时间线校准。仅用设备标称带宽去除数据量,通常只得到理想传输部分。

小块随机访问还要检查 IOPS。固定 I/O 大小时,有 B≈IOPS×IO sizeB\approx\mathrm{IOPS}\times\mathrm{IO\ size};例如 100 万次/s、每次 4 KiB 对应 4.096 GB/s。能否达到这个吞吐,还取决于访问时延、队列深度、并发度和设备内部行为。

恢复前缀与每步加载 KV,需要分别建模

“KV Offloading”容易覆盖两种不同情形。

一种是扩大可复用前缀缓存:把已完成的前缀块留在主机内存或更外层,未来请求命中后取回,减少 Prefill。另一种是扩展当前活跃请求的工作集:Decode 过程中,每步按需从外层加载参与 Attention 的 KV。后者可能在每个输出 Token 的关键路径上产生流量。

vLLM v0.30.0 的 OffloadingConnector 文档描述了前一种用途:完成的块进入更大层级,命中后提升到 GPU。它不能直接作为“任意活跃 KV 都能透明流式执行”的证据。下一篇讨论的稀疏选块与预取,则属于需要额外设计的运行时场景。

缓存命中后,仍要与重算比较

取一个不考虑重叠的教学算例:完整 Prefill 为 100 ms;恢复命中前缀需要读取 1 GB;剩余计算为 30 ms;另有 5 ms 的恢复开销。

Tcached=1 GBBeffective+30 ms+5 msT_{\mathrm{cached}}=\frac{1\ \mathrm{GB}}{B_{\mathrm{effective}}}+30\ \mathrm{ms}+5\ \mathrm{ms}
有效恢复带宽 纯读取耗时 缓存路径总耗时 对比完整重算
10 GB/s 100 ms 135 ms 更慢
20 GB/s 50 ms 85 ms 更快

两者持平所需的带宽约为:

Bbreak even=1 GB100−30−5 ms≈15.4 GB/sB_{\mathrm{break\ even}}=\frac{1\ \mathrm{GB}}{100-30-5\ \mathrm{ms}} \approx15.4\ \mathrm{GB/s}

分母若不为正,单靠提升传输带宽无法让这个串行路径优于重算。命中率提高也不保证耗时下降:计算可能减少,但要恢复的 KV 字节可能增加。

如果部分读取与计算重叠,应看数据的使用截止时间。设实际 I/O 时间为 TIOT_{IO},从发起加载到数据必须可用之间有 WW 的独立计算窗口,则额外等待可粗估为 max⁡(0,TIO−W)\max(0,T_{IO}-W)。只有不依赖这批待加载数据的工作才能进入 WW;不能把整次前向计算时间都算成可隐藏窗口。

用 PCS 时,先读清楚统计范围

PCS 将这些问题拆成容量计算、Footprint、Bandwidth Envelope 和 Cache Replay。不同页面的数字回答不同问题:

口径或模式 需要核对的含义
Reference / Payload 按指定模型与精度计算的有效数据
Serialized 加入量化元数据、Padding 等后的存储表示
Reserved / Active execution 当前执行或分配策略需要保留的空间
Prefix Store / Replay resident 前缀对象的存储口径,以及回放后实际仍驻留的对象
Bandwidth Envelope 在给定前缀命中假设下,恢复字节与计算节省是否匹配
Cache Replay 多请求竞争、共享、保护与驱逐之后,还能命中多少

对单个序列成立的命中比例,不能直接当成有限容量缓存面对整个请求流时的实际命中率。同样,Payload 缩小一半,也不保证预留容量、恢复流量和端到端时延一起减半。

对于复杂缓存结构,应按组件分别统计存储条目数、每条目字节与保留范围,再加总。压缩 KV、窗口状态、索引、递归状态和量化 scale 不一定具有相同的增长规律。

当容量预算、传输路径和使用截止时间都明确后,才能决定哪些 KV 留在快层,哪些可以按需取回。接下来的问题是:Top-K 给出一组块号,运行时具体怎样找到并准备这些数据?

上一篇:KV Cache 内存布局 · 下一篇:从 Top-K 到数据就绪:多级 KV 驻留与预取。

评论