KV Cache 预算:并发、上下文与块大小

用可复算的字节公式估算长上下文服务容量,并解释分页的尾部浪费与容量边界。

展开文章目录

这篇实验笔记只做一件事:把 KV Cache 容量算到可以复核。默认参数取 32 层、8 个 KV heads、head 维度 128、FP16、8K 上下文和 16 路并发。

计算

每个 token 的 KV 字节:

32×8×128×2×2=131072 B=128 KiB32 \times 8 \times 128 \times 2 \times 2 = 131072 \text{ B} = 128 \text{ KiB}

整批缓存:

128 KiB×8192×16=16 GiB128 \text{ KiB} \times 8192 \times 16 = 16 \text{ GiB}

这个结果还没有算模型权重、激活、allocator 和临时 workspace。它说明长上下文服务不能只按“模型能否装进显存”判断并发上限。

分页的尾部

若块大小为 16 token,8K 恰好整除,尾部浪费为零。把上下文改成 8,001 token,则需要 501 个块,最后一块只使用 1 个 token:

浪费比例=158016≈0.19%\text{浪费比例} = \frac{15}{8016} \approx 0.19\%

单个请求的浪费不大,但当块更大、序列更短、并发更高时,累计开销会变得可见。分页的价值更多在外部碎片和共享,而不是消灭块内浪费。

运行实验

打开 KV 容量账本,调整 GQA 的 KV heads、上下文和并发。观察两个问题:

  1. 把 KV heads 从 32 降到 8,容量是否按比例下降?
  2. 把并发翻倍,尾延迟风险为什么不能只用容量是否“还装得下”回答?

模型结果只作为量级估计。真实服务要用目标框架的显存报告和压力测试校准。

公式与默认参数

实验使用的基本公式是:

B=L×Hkv×dhead×2×bB = L \times H_{\text{kv}} \times d_{\text{head}} \times 2 \times b

其中 BB 是每个 token 的缓存字节数,LL 为层数,HkvH_{\text{kv}} 为 KV heads,dheadd_{\text{head}} 为 head 维度,系数 2 对应 K 和 V,bb 是每个元素的字节数。总容量再乘以上下文长度与并发数:

M=B×T×CM = B \times T \times C

默认的 32 层、8 个 KV heads、128 维、FP16、8K 上下文和 16 并发会得到约 16 GiB 的缓存。这个数字只包含 KV,不包含权重、激活、块表、对齐和临时 buffer,因此不能把它当作模型的完整显存需求。

怎么读指标

页面给出的三个指标对应三个决策:

  1. 每 token 缓存:模型结构和精度决定的下限,改变并发不会改变它。
  2. 总缓存:上下文与并发的乘积,服务容量规划更需要这个数。
  3. 每 MiB 可存 token:把容量换算成直观的 token 数,便于比较不同模型。

把 KV heads 从 32 改成 8,每 token 字节数应按 4 倍下降;把上下文从 8K 改成 16K,总缓存应约翻倍;把并发翻倍,总缓存也约翻倍。若界面结果偏离这些比例,通常是输入或单位理解错了。

什么时候模型会骗人

这是一个纯预算模型,无法告诉你 GPU 上真实的吞吐、allocator 碎片或 kernel 延迟。它也不模拟块共享、prefix caching、KV 驱逐和量化误差。对于使用 PagedAttention 的服务,实际容量可能因为块大小和请求长度分布而低于理论值;对于使用量化 KV 的服务,每个元素字节数变化还需要考虑反量化成本。

如果要用它做面试或方案评审,建议先算一个下限,再用目标框架测量一个上限。两者之间的差距,才是分页、调度和缓存策略能发挥作用的空间。把估算值直接写成“实测显存占用”会误导读者,也会掩盖真正的工程问题。

一个灵敏度练习

先固定 8K 上下文和 8 个 KV heads,把层数从 16 提到 64,观察缓存如何线性变化;再把 head 维度从 64 提到 256,体会为什么“模型看起来不大”不代表 KV 压力小。最后保持总 token 数不变,交换并发和上下文,看总缓存相同但尾延迟风险是否相同。答案是否定的:长序列请求更难调度,块池回收也更慢。

这个实验的目的不是给出另一个固定公式,而是训练一种预算直觉:每当你准备增加上下文或并发时,先问需要多少字节、这些字节放在哪个内存层、当请求失败时系统如何回收它们。

把结果写进设计文档

一份可评审的 KV 预算至少应包含模型结构、精度、上下文上限、并发目标、每 token 字节数、总容量和保留余量。若总容量接近目标显存,应明确降级顺序:降低并发、缩短上下文、切换更低精度 KV、启用分页共享,还是拒绝新请求。不同降级对质量和用户可见行为的影响不同,不能只留下一个“可能 OOM”的模糊结论。

还要区分平均与尾部。平均请求长度 2K 不代表 32K 请求不会出现;长请求会在块池中驻留更久,并拖慢回收。用 P95/P99 上下文和并发做预算,比用平均值更接近真实服务。预算模型的价值在于提前暴露这种差距,而不是给出一个看似精确的单一数字。

如果预算结果刚好接近上限,不要把它当成“可以通过”。至少给块表、对齐、临时激活和流量突发留出余量;在压测中观察拒绝率与 GC/释放行为,而不是只相信理论公式。一个安全的容量设计会明确在超限时限制上下文、降低并发还是延迟新请求。

在团队评审中,可以把“每 token 128 KiB”当作一个可验证断言:检查层数、KV heads、头维度和类型,手算一次,再用实验页复算。如果两个结果不一致,优先检查是否把注意力头总数当成 KV heads,或把字节数和 bit 数混在一起。这个简单的算术检查能提前发现大量容量规划错误。

最后把容量预算与成本预算分开记录:显存够不够是一回事,单位 token 的成本能不能覆盖业务是另一回事。两者都通过,服务才具备可持续性。

这条记录也应随模型版本和框架配置一起更新,不能沿用上一版估算。

把估算过程写进设计文档,后来的人才能知道容量数字从何而来,并在模型升级时快速重新计算。

这张表也应随着并发目标和上下文策略一起变更。

解释图逻辑上下文如何落到物理 KV 块
  1. 01请求token 序列
  2. 02逻辑块连续编号
  3. 03块表映射物理页
  4. 04KV 页非连续分配
  5. 05调度器共享与回收

分页把连续上下文需求与物理显存分配解耦,收益由请求长度分布和块大小共同决定。

证据清单

来源与适用范围

  1. 论文
  2. 文档

限定条件:本地确定性预算模型;不包含 GPU 实测,也不模拟 allocator 元数据和临时激活。

本文遵循 CC BY-NC-SA 4.0

站点代码采用 MIT。所有模拟结果都标注了假设,不替代目标环境实测。

继续阅读归档