# 从一次请求到可持续使用的服务

开场的“当次 K/V”在这里成为需要分配、增长和释放的实际数组。课堂先走已知输入的 prefill 与逐 token 的 decode，再看缓存块表和请求接续。本篇保留容量公式、前缀复用、跨资源传输与推测解码，按它们各自解除的瓶颈组织。

模型权重相同，不同输入仍会产生不同激活和 K/V。服务系统要同时安排这些状态、模型计算和请求等待。判断系统是否有效，不能只看每秒总 token 数：用户多久看到第一个 token、之后是否连续、内存是否会超限，同样决定它是否可用。

<a id="prefill-decode"></a>
## prefill、decode 与时间指标

prefill 处理已经给出的输入。输入各位置已知，因而能组织并行计算，但因果 mask 仍限制各位置的可见前缀。decode 面对未知的后续输出，普通自回归过程先选择当前 token，再以它为条件计算下一步。因果计算使旧位置不依赖后来才生成的token，所以在模型、位置等计算条件兼容时，旧位置各层K/V可复用。新token仍要计算自己的Q/K/V，用当前Q读取旧K/V，并将新K/V追加到本层缓存；逐层都要执行这一过程。无需持久缓存所有旧Q，但“不重算旧K/V”不等于“不再读取旧K/V”。缓存长度随实际进入模型的token增长；刚选出尚未送入下一次前向的token，还不能提前算作已经有了各层K/V。

首 token 延迟（TTFT）从请求到达计到第一个输出，包含可能的排队及 prefill；后续 token 延迟反映持续生成的节奏。吞吐描述总体完成量，goodput 则关注满足指定服务约束的有效完成量。不同定义和测量区间必须明确，否则系统 A 的“更快”可能只是以更长等待换更大批量。典型长输入 prefill 常有较大计算密度，小批 decode 常受到权重与 KV 读取影响，但必须在具体模型、形状、batch、精度和硬件上测量，不能先验贴死瓶颈标签。

<a id="kv-capacity"></a>
## KV 容量的完整口径

本课用独立教学配置：32 层，8 个 KV heads，每组维度 128，K/V 都用 BF16、每数 2 字节。T 个已缓存 token 占：

$$M_{KV}=2\times L\times T\times h_{KV}\times d_{head}\times b.$$

第一项 2 来自 key 和 value 两份；b=2 才是每个数的字节数。代入一 token 得 131072 bytes=128 KiB。8192 token 为 1 GiB，32768 token 为 4 GiB；八个互不共享、各 8192 token 的请求合计 8 GiB。T 包含进入缓存的输入与生成部分，生成继续时仍会增长。GiB=2³⁰ bytes，不能与权重账本的十进制 GB 混为相同单位。

这份配置可以有 32 个 query heads，但只存 8 组 K/V，即分组查询注意力 GQA 的一种设定。缓存按实际 K/V 组数计算，不再乘 query 头数。普通多头注意力的 KV 组数常与 query 头数相同，多查询注意力可共享一组 K/V；具体模型必须读配置。本算式还未计入分配空槽、块表、临时工作区、量化元数据或通信缓冲。

<a id="batching-paging"></a>
## 连续批处理和分页为什么相连

不同请求的输出长度不同。如果整批全部完成才能接纳新请求，短请求退出后的容量会暂时空闲。连续批处理在生成迭代之间重新组织活跃请求，让新请求进入、完成请求退出。课堂固定A=2步、B=4步、C=2步以及两个槽位。等待整批时，前两轮A/B同在，第3–4轮只有B，第5–6轮才由C进入；逐轮接纳时，前两轮A/B同在，第3–4轮B/C同在，A退出后的下一轮就接C。六轮与四轮的对比只展示相同需求下的空位利用；它假设新请求入场准备已完成，不包含真实 prefill 时间，所以不能用轮数比宣布 GPU 加速倍数。

请求持续进出，KV 也持续增长，预留完整连续区域容易浪费。PagedAttention 用块表把逻辑 token 块映射到物理存储块。本课块长 4，长度 6 占两块，逻辑 0/1 可映射物理 7/1；到第 9 token 再分配逻辑 2→物理 5。一般需要 ceil(T/B) 块，末块空槽少于 B。块小可能降低尾部浪费，却增加管理及访问开销；4 是教学数值，不是生产默认推荐。

PagedAttention 解决 KV 分配和寻址，FlashAttention 优化 attention 算子中的数据访问，两者可以结合。[vLLM 原论文](https://arxiv.org/abs/2309.06180)和课堂 A14 说明了这一管理机制。真实系统还可能有滑动窗口、混合注意力层等不同状态结构，不能认为所有模型的 allocator 都是同一种简单二维数组。

<a id="prefix-cache"></a>
## 前缀复用与文档拼接的边界

N56采用两个请求共享4096 token前缀、各有2048 token独有尾部的构造。每token为128 KiB时，不共享占2×6144×128 KiB=1.5 GiB；共享占(4096+2×2048)×128 KiB=1 GiB，节省0.5 GiB。共享块需要引用管理，分叉后的内容分别计算，完成请求释放引用。复用还可减少重复 prefill，但不取消后续 decode。

直接缓存复用要求精确 token 前缀以及模型、适配器、位置处理等相关计算条件一致；语义近似不是命中依据。[SGLang 的 RadixAttention 作者说明](https://www.lmsys.org/blog/2024-01-17-sglang/)展示了前缀树式的组织与复用，[vLLM 自动前缀缓存文档](https://docs.vllm.ai/en/latest/features/automatic_prefix_caching/)提供另一实现入口。调度时也可考虑缓存在哪台实例上，但命中收益需要与排队和负载不均衡比较。

独立文档 A、B 各自算出的 KV，一般不能直接拼接成输入 A 后接 B 的 KV。因果自注意力中，B 的 token 在连续输入下可读取 A，其后续层表示会改变；独立计算 B 时不存在这些依赖。A11 中所谓文档间 cross-attention 指这里的跨文档自注意力依赖，不能误当 encoder-decoder 架构中的独立 cross-attention 模块。CacheBlend 等方案通过选择性重算来处理复用与依赖之间的矛盾，其质量与速度需按方法条件验证。缓存压缩如 CacheGen 同样要评价恢复成本与数值影响，不应把缓存复用一概叫“完全免费且无损”。

<a id="disaggregation"></a>
## 两阶段分离，以及 GPU 外的状态

长 prefill 可能挤占正在 decode 的资源，影响出字间隔。将两阶段分配给不同资源可减少干扰并分别优化批量，但必须把 prefill 建立的 KV 交给 decode。传输字节数、网络带宽、排队、缓存位置和负载都进入收益判断。低负载、短输入或慢网络下，额外传输可能不划算；分块 prefill、混合调度也是可比较的简单方案。

[NVIDIA Dynamo](https://docs.nvidia.com/dynamo/latest/knowledge-base/overview)是分布式推理运行时的例子，承担工作者之间的路由、协调等职责，prefill/decode 分离是其可支持的组织之一，不是它唯一含义。LMCache 关注 GPU 外 KV 的保存、恢复和相关复用；容量从 GPU 扩到主机或其他存储时，需要付出搬运与恢复时间。[Mooncake](https://github.com/kvcache-ai/Mooncake/)围绕 KV 池化与传输等组件组织分布式服务。它们不是每个部署必须顺次安装的三个软件，可按所需职责组合。接口、兼容性和真实性能应绑定部署版本与硬件，课堂只定位机制。

<a id="speculation"></a>
## 推测解码：为什么“验证”还需要纠正

推测解码先让较便宜的草稿模型提出若干候选 token，再让目标模型批量检查。它有机会利用一次目标执行验证多个位置，收益取决于草稿成本、接受长度、验证批量和设备；草稿总是更快或接受率总高都不是无条件事实。EAGLE 等后续方法改变草稿的构造方式，具体接受和验证条件仍要单独核对。

对于随机采样，[Leviathan 等的精确推测采样](https://proceedings.mlr.press/v202/leviathan23a.html)不只比较两个 argmax。令当前前缀下目标分布为 p，草稿分布为 q，草稿抽到 x 后以 min(1,p(x)/q(x)) 接受。若拒绝，必须从与 max(0,p−q) 成比例的残差分布采样纠正。候选来自 q，所以涉及的 q(x)>0；当 p=q 时没有拒绝事件，无需定义零总量残差。顺序接受到第一次拒绝为止，后续基于错误草稿前缀的候选丢弃并重新推进；整段都接受时还可用目标分布产生下一 token。

D2 算例令 p=(0.6,0.3,0.1)，q=(0.5,0.2,0.3)。A、B 的接受率为 1，C 的接受率为 1/3。直接接受贡献为 (0.5,0.2,0.1)，总拒绝概率为 0.2。残差 p−q 的正部为 (0.1,0.1,0)，归一化得 (0.5,0.5,0)；纠正贡献为 (0.1,0.1,0)。两者相加恰好恢复 p。若拒绝后随意再从 p 抽样，则一般不能得到相同目标分布。

这个证明针对正确计算和采样条件下的目标分布；浮点实现、采样参数及近似草稿机制仍需验证。贪心模式可以用目标选择进行逐项接受，但不能把简单 argmax 比对套成随机采样无损证明。推测解码保留为本篇的展开机制，不要求默认课堂逐项经过。

<a id="rollout"></a>
## 服务为什么也属于训练成本

RL 后训练常反复生成候选、评分和更新。候选生成即 rollout，同样消耗 prefill、decode 与 KV 资源。服务引擎提高生成吞吐，可以改变训练流程成本，但不会自动修复奖励错误、无效候选或不稳定更新。候选生成、评价和参数更新是不同动作；普通用户的一次请求或反馈不会在本图中自动成为梯度。必须定义训练数据、目标并实际执行优化，才连接到下一版参数。训练目标与系统效率应分别评价，再看它们共同决定的实际收益。

课堂入口：[N52请求开始](../classroom/N52.html?step=b0)、[N53逐token缓存](../classroom/N53.html?step=b0)、[N54容量](../classroom/N54.html?step=b0)、[N55分页](../classroom/N55.html?step=b0)、[N56双请求共享](../classroom/N56.html?step=b0)、[N57–N58调度](../classroom/N57.html?step=b0)、[N59阶段压力](../classroom/N59.html?step=b0)、[N60训练回路](../classroom/N60.html?step=b0)。系统管理包住prefill与decode，并负责状态的分配、传递、复用和释放，不是生成结束以后才追加的孤立模块。原论文、课程材料的具体版本及采用位置见 [来源清单](../sources.html)。教学计算与硬件性能实测分别报告。


## 把这个问题带进实验

打开对应 Notebook 后，找到下列同名小节。各本的准备说明、已有输出和证据边界保留在本册中。

- [LAB 06 · R4 · 共享权重，为什么八份上下文仍占八份空间？](https://deepnote.com/workspace/ise3309jingao-5ba66b0a-4283-4f5a-ae06-3f68d3201e99/project/Fall-26-Lecture-06-2ff4b87b-2904-4b9c-b9e9-bdbd72962691/notebook/LAB-06-56ea4de667c74a84a7be00689c8de2d9?utm_source=openai&utm_medium=mcp&utm_campaign=openaimcp&utm_content=56ea4de667c74a84a7be00689c8de2d9&utm_term=get_notebook)
- [LAB 06 · R5 · 先列可行集，再作一次真正会翻转的选择](https://deepnote.com/workspace/ise3309jingao-5ba66b0a-4283-4f5a-ae06-3f68d3201e99/project/Fall-26-Lecture-06-2ff4b87b-2904-4b9c-b9e9-bdbd72962691/notebook/LAB-06-56ea4de667c74a84a7be00689c8de2d9?utm_source=openai&utm_medium=mcp&utm_campaign=openaimcp&utm_content=56ea4de667c74a84a7be00689c8de2d9&utm_term=get_notebook)

[查看全部实验与课堂页对应](../labs/index.html)
