一次并行处理整段 prompt
所有 prompt token 同时经过各层,计算 self-attention;输出最后位置的 logits,同时缓存每一层的 K/V。通常更偏 compute-bound,决定 TTFT 的主体。
tokens_compute = prompt_length
ARCHITECTURE / INFERENCE ENGINE
标准全因果 Attention 的 prompt 可以并行计算并写入 KV Cache;常规 decode 每轮每请求输入一个新 token,并读取此前可见位置的 K/V。 prefill 与 decode 的计算形态不同,瓶颈还取决于 batch、序列长度、结构与硬件。
同一组权重,处理 token 的方式不同:prefill 并行吃整段 prompt,decode 逐个追加。
所有 prompt token 同时经过各层,计算 self-attention;输出最后位置的 logits,同时缓存每一层的 K/V。通常更偏 compute-bound,决定 TTFT 的主体。
tokens_compute = prompt_length
首 token 由 prefill 的最后位置 logits 采样。decode 将它作为下一次输入,逐层计算新 Q/K/V、读取历史 KV 并追加本位置的 K/V,再预测后一个 token。小 batch 常受权重与 KV 读取带宽限制。
new_tokens = 1 per decode step
下面按 forward 步骤展示输入位置数、已生成 token 数和缓存长度。
固定等长 batch、无 EOS 提前停止,最多展示 40 个输出。每次 forward 后立刻采样;因此已生成 G 个 token 时,缓存长度是 P+G−1。真实 serving 还需处理分块 prefill、动态批处理与分页。随机采样时还应核对随机流是否跟随请求身份。
EXECUTION STATE
标准 MHA 每层保存 K 与 V。GQA/MQA 让多个 query heads 共享 KV heads,缓存按共享后的数量计算。下面假设各层配置相同、K/V 的 head dim 相同、每请求保留 S 个位置且不跨层共享;滑窗、MLA、层间共享及量化元数据需另算。
ATTENTION PRESET
KV bytes = B × S × L × 2 × Hkv × dhead × bytes
大 O 记号帮助看增长趋势,但真实延迟还受内存层次、kernel、batch 与序列调度影响。
投影与 MLP 是大的矩阵乘,GPU 利用率高;self-attention 的 token 交互项随 S² 增长。长 prompt 是吞吐与 TTFT 的主要压力。
attention interaction ≈ O(S² d)
单 token 的 Q 很小,历史 K/V 读取量随 S 增长。GPU 可能更多在等内存带宽,而不是算术单元;批处理可提高总吞吐但增加缓存。
KV read ≈ O(S · L · Hkv · dhead)
decode 每生成一个 token 都要遍历 L 层投影、MLP/MoE 与最终 head。权重读取通常占固定大块成本,MoE 可减少每 token 的专家计算但增加路由。
layers = L every token
这些方法主要改进请求调度、缓存管理和执行效率。
一个请求结束后立刻释放槽位,不等整批结束。prefill 与 decode 请求可以交错调度,提高 GPU 利用率。
把每条序列的 KV 切成块,按需分配与回收。减少内存碎片和过度预留,也方便连续批处理与共享 prefix。
还必须匹配实际前缀 token/模态输入及其顺序、模型与 adapter 权重、位置和 attention 配置等缓存键;只比较用户文本不够。命中缓存能省前缀计算,但存储、淘汰与权限隔离仍需设计。
避免一个超长 prefill 独占 GPU 很久,让正在 decode 的请求也能获得执行机会,但会增加调度与时间指标复杂度。
用 draft 模型提出多个 token,再由目标模型并行验证接受。收益取决于接受率、验证成本和草稿模型的额外计算。
FP8/INT8 缓存可减少带宽与显存,但可能影响长上下文质量;还要注意 scale、分组与 kernel 支持。
静态波次等待当前一组全部结束,再接纳下一组;连续批处理允许在迭代边界加入新请求。分块 prefill 让长输入分几轮处理,可与 decode 共享调度预算。预算单位与优先级由具体系统定义。
峰值已占用块:;当前预留容量(含共享块):
| 请求 | 到达 tick | 状态 | 输入已处理 | 输出已采样 | 活动 KV 位置 | 首 token 等待 | 完成耗时 |
|---|
A:(到达0,输入8,输出4),B:(1,8,3),C:(2,4,2);A/B 共享前4个 token。FIFO 接纳,decode 优先,每请求每轮至多一个 decode;输出长度预先固定。缓存达到 P+G−1,完成后释放私有块。
为了避免小容量下出现无可分配块的僵持,面板在接纳时为请求的已知最大长度预留容量额度,但物理块仍随 token 按需分配;预留额度与已占用块分别显示。真实引擎可能采用抢占、交换或重算等策略,本例没有实现这些策略,也没有把 tick 换算为毫秒。
开启前缀时,预热块占一个物理块,生成它的先前成本不计入本队列。它只在 A/B 活跃时有活动引用,两个请求结束后仍作为可复用缓存保留。这里不继续缓存新产生的私有块,不模拟 LRU 驱逐或多租户哈希。
同样文本未必同样 token。模型版本、adapter、位置、模态输入和缓存隔离信息都可能影响键;块内容还依赖此前前缀。命中前必须确认上下文计算条件兼容。
请求结束可以减少引用计数。引用为零的块可被保留为缓存,也可被回收覆盖;仍被其他请求使用的块不能直接覆盖。
完整前缀块可供多个请求只读使用。分支继续生成需要私有尾块;如果系统允许共享未满尾块,写入前通常需要复制或其他隔离机制。
命中的前缀 K/V 可复用,后续 query 仍要读取相关历史状态。Prefix cache 节省的主要是相同前缀的重复 prefill;实际时延还取决于查找、搬运和剩余计算。
单请求缓存按已输入位置计算。队列模拟最多两请求、已知输出长度、接纳时预留容量、按需分配物理块;共享块预热,不模拟 LRU、抢占或真实毫秒。