AI 工程基础体系 · 第 81/100 篇。内容覆盖机器学习、深度学习与生成式 AI;模型、数据、评测、权限和成本会作为同一生产系统处理。
LLM 连续批处理:调度、Prefill、Decode、公平性与吞吐延迟
LLM 服务中的“批处理”不是一次把一组请求拼成矩阵、等待整组请求全部结束,再处理下一组。对自回归生成模型而言,不同请求的输入长度、输出长度、到达时间和取消时间都不同。如果仍然采用固定批次,短请求必须等待长请求结束,GPU 也会在部分序列完成后出现空槽。
连续批处理(continuous batching)把调度粒度从“一整个请求批次”降低到“生成迭代”或“解码步”:每轮只要有请求完成、取消或新请求进入,就重新决定下一轮 GPU 应处理哪些序列。因此,已完成的请求可以立即退出,新请求可以补入正在运行的批次。
但连续批处理并不只是一个队列技巧。它同时涉及:
- Transformer 的 Prefill 和 Decode 两种计算阶段;
- KV Cache 的显存占用和生命周期;
- GPU kernel 的批量执行方式;
- 新请求与已有请求之间的调度竞争;
- TTFT、TPOT、端到端延迟和吞吐之间的取舍;
- 多租户配额、优先级、公平性以及取消和故障恢复。
下面从模型计算开始,逐步建立这些概念之间的因果关系。
1. 自回归 Transformer 为什么天然分成 Prefill 和 Decode
1.1 因果自注意力的计算对象
Transformer 的自注意力通常写为:
其中:
- 是查询矩阵;
- 是键矩阵;
- 是值矩阵;
- 是每个注意力头的维度;
- 是因果掩码,禁止位置 读取未来位置 的信息。
对于长度为 的输入,因果掩码允许第 个位置看到从第 1 个到第 个位置的内容。模型因此可以一次并行计算整个输入序列的隐藏状态,同时保证每个位置只依赖过去信息。
生成阶段则不同。假设模型已经处理了提示词和此前生成的 token,现在只需要为下一个 token 计算一个新位置。这个新位置的查询向量 需要访问此前所有位置的 和 ,但不需要重新计算旧位置的 和 。
这就形成了两个阶段。
1.2 Prefill:处理输入提示词
Prefill 是把用户输入的 prompt 送入模型,计算所有输入 token 的隐藏状态,并产生它们对应的 。
例如输入:
请解释 TCP 拥塞控制
经过 tokenizer 后得到 个 token。Prefill 会并行处理这些 token,建立该请求的初始 KV Cache。Prefill 结束后,模型通常可以得到第一个待生成 token 的 logits。
Prefill 的特点是:
- 输入 token 数量通常较多;
- 同一请求内存在较强的矩阵并行性;
- 需要建立一段长度为 的 KV Cache;
- 计算和显存带宽压力通常较高;
- Prefill 时间直接影响首 token 延迟,也就是 TTFT。
Prefill 并不是“模型理解完输入但没有输出”。从计算角度看,它已经完成了最后一个输入位置的前向计算,并可以据此采样第一个输出 token;服务系统是否把这个 token 在同一轮立即发出,取决于具体执行器。
1.3 Decode:逐 token 生成
Decode 是在已有 KV Cache 的基础上,每次输入一个新 token,生成下一个 token。
设某个请求当前已有 个 token 的 KV Cache。下一轮 Decode 只需:
- 读取上一个 token;
- 计算这个新位置的 ;
- 使用 对长度为 的历史 做注意力;
- 产生下一个 token;
- 把当前新位置的 追加到 KV Cache。
Decode 每次通常只处理一个新 token,因此单个请求的矩阵规模较小,计算往往受显存读写和 KV Cache 访问限制。多个请求的 Decode 可以拼成 batch,以提高 GPU 利用率。
1.4 为什么 Decode 可以复用 KV Cache
如果每轮 Decode 都重新对完整序列做注意力,那么生成长度为 的输出时,需要重复处理历史 token,计算量会显著增加。
KV Cache 保存每层、每个历史位置的 和 。下一轮只需要新增一个位置的 ,并让新查询向量访问缓存中的历史键和值。
对于使用 层、 个 KV 头、每头维度 、数据类型每元素占 字节的模型,单个 token 的 KV Cache 近似占用:
其中前面的 2 表示 和 。
一个请求的缓存占用近似为:
其中:
- 是输入 token 数;
- 是已经生成的 token 数;
- 如果按最大输出长度预留,还需要把 换成预计的最大输出长度。
使用 Grouped-Query Attention 或 Multi-Query Attention 时, 小于查询头数,因此 KV Cache 会显著小于标准 Multi-Head Attention。这是模型结构和服务系统内存规划之间的直接联系。
2. 静态批处理与连续批处理
2.1 静态批处理的同步边界
传统静态批处理通常执行如下流程:
- 收集一批请求;
- 对整批请求做 Prefill;
- 对整批请求做一次 Decode;
- 继续 Decode,直到整批请求全部完成;
- 批次结束后才接收下一批请求。
假设一个 batch 中有三个请求,其输出长度分别为 2、20、5。Decode 第 2 步之后,第一个请求已经完成,但静态批处理仍可能保留它对应的 padding 槽位,或者等待整个 batch 中最长请求完成。
这种方式的主要问题是批次内同步:最慢的请求决定批次的生命周期。
2.2 连续批处理的迭代级调度
连续批处理把每一轮 Decode 视为一个重新调度点:
- 移除已经生成 EOS、达到长度上限或被取消的请求;
- 更新仍在运行请求的状态;
- 从等待队列选择可以进入的请求;
- 决定本轮执行哪些 Prefill chunk 和 Decode;
- 执行 GPU 前向;
- 采样 token、更新 KV Cache 和流式输出;
- 进入下一轮。
因此,批次不是一个固定集合,而是一个随时间变化的集合:
例如:
flowchart LR
Q[等待队列] --> S[调度器]
S --> P[Prefill 队列]
S --> D[Decode 活跃集合]
P --> X[GPU 执行器]
D --> X
X --> K[KV Cache]
X --> O[采样与流式输出]
O --> F{完成/取消/继续}
F -->|继续| D
F -->|完成或取消| R[释放槽位与 KV Cache]
R --> S
关键路径是:等待队列中的请求不会直接访问 GPU,而是必须先通过调度器;调度器不仅选择请求,还必须保证显存预算、批次 token 预算和租户配额都满足。
“连续”不意味着请求可以在任意 GPU 指令执行到一半时插入。常见实现仍然在 kernel 或一次执行迭代之间切换。请求通常只能在调度边界加入,已经发射的 kernel 不能被普通新请求安全地强行打断。
3. 调度器需要维护什么状态
一个生产级请求至少需要以下状态:
WAITING
└─> PREFILLING
└─> DECODING
├─> FINISHED
├─> CANCELLED
├─> FAILED
└─> PREEMPTED / RECOMPUTE_REQUIRED
每个请求还需要记录:
- 请求到达时间;
- tokenizer 后的 prompt token 数;
- 已完成的 Prefill token 数;
- 已生成 token 数;
- 最大新 token 数;
- 停止词、EOS 和采样参数;
- KV Cache 的物理块或页;
- 所属租户、优先级和配额;
- 客户端连接是否仍然有效;
- 累计输入 token、输出 token 和推理成本。
请求完成的判定不能只依赖 EOS。以下情况也可能结束请求:
- 达到
max_new_tokens; - 生成了用户指定的 stop sequence;
- 客户端取消;
- 租户超时或预算耗尽;
- 模型执行失败;
- worker 进程退出。
如果只处理正常 EOS,取消请求留下的 KV Cache 会造成显存泄漏,最终表现为服务吞吐逐渐下降,随后发生 OOM。
4. Prefill 与 Decode 如何共同占用 GPU
4.1 统一的 token budget
调度器通常需要限制每轮处理的 token 数,例如:
这里的 token 是执行预算,不是简单等价的计算量。一个 Prefill token 通常比一个 Decode token 具有更高的计算和内存代价,因此真实执行器可能对两者使用不同权重:
其中 、 是经验或基准测试得到的成本权重, 是每轮预算。
仅用“请求数”限制 batch 大小通常不够。例如两个请求:
- 请求 A:prompt 8,000 token;
- 请求 B:prompt 20 token。
它们都是两个请求,但 Prefill 的计算、显存写入和 TTFT 影响完全不同。生产调度一般同时考虑:
- 活跃请求数;
- 本轮 Prefill token 数;
- 本轮 Decode token 数;
- KV Cache 总 token 数;
- 模型权重和临时激活占用;
- GPU 显存安全余量。
4.2 Chunked Prefill
如果一个 prompt 很长,一次性 Prefill 会占满整个执行轮次,正在 Decode 的请求就可能长时间得不到下一个 token。Chunked Prefill 将长 prompt 拆成多个片段,例如每次处理 512 或 1,024 个 token。
设请求 A 的 prompt 长度为 2,048,chunk 大小为 512,则 Prefill 可能经历:
第 1 轮:处理 [0, 512)
第 2 轮:处理 [512, 1024)
第 3 轮:处理 [1024, 1536)
第 4 轮:处理 [1536, 2048)
每个 chunk 处理后,KV Cache 增加对应长度。请求在 Prefill 未完成前通常不能进入普通 Decode 阶段,但调度器可以在两个 chunk 之间插入已有请求的 Decode。
需要注意,Prefill chunk 的边界不能破坏因果关系。后一个 chunk 的 token 必须能够访问前面 chunk 已经写入的 KV Cache;因此,chunked Prefill 不是把 prompt 拆成相互独立的批次,而是把同一序列的计算分阶段完成。
5. 一个完整的调度算例
为了说明调度过程,采用一个简化模型:
- 每轮预算为 4 个“执行 token 单位”;
- 一个 Decode token 占 1 单位;
- Prefill 每个 chunk 最多 2 个 token;
- Prefill 与 Decode 可以在同一轮混合;
- 新请求只有在它的 Prefill 完成后才能 Decode;
- 这是用于理解的离散模型,真实 GPU 上 Prefill 和 Decode 的代价并不相同。
三个请求如下:
| 请求 | 到达时间 | Prompt 长度 | 最大输出 |
|---|---|---|---|
| A | 0 | 4 | 4 |
| B | 1 | 2 | 2 |
| C | 2 | 2 | 1 |
采用“先推进等待中的 Prefill,再填充 Decode”的简单策略:
| 轮次 | 到达请求 | 本轮动作 | 结果 |
|---|---|---|---|
| 0 | A | A Prefill 2 | A 剩余 Prefill 2 |
| 1 | B | A Prefill 2、B 尚未处理 | A Prefill 完成 |
| 2 | C | B Prefill 2、A Decode 1 | B Prefill 完成,A 输出第 1 个 token |
| 3 | — | C Prefill 2、A Decode 1、B Decode 1 | C Prefill 完成,A 输出第 2 个,B 输出第 1 个 |
| 4 | — | A Decode 1、B Decode 1、C Decode 1 | C 完成,A 输出第 3 个,B 输出第 2 个并完成 |
| 5 | — | A Decode 1 | A 输出第 4 个并完成 |
这个例子展示了连续批处理的两个性质:
- A 不需要等待 B 和 C 的完整生命周期结束;
- B 和 C 加入后,活跃 Decode 集合从
{A}变为{A,B},再变为{A,B,C},最后逐步缩小。
如果采用静态批处理,A、B、C 可能先被放入同一个 batch,并且 Decode 轮次需要按照最长输出长度执行;B 和 C 完成后留下的槽位不能立即服务新请求。连续批处理通过每轮移除已完成请求,减少了这种浪费。
但该策略也有问题:在第 0、1 轮,A 的 Prefill 占用了调度机会;如果不断有长 prompt 到达,并且调度器始终优先 Prefill,已经进入 Decode 的旧请求可能长时间无法获得下一个 token。
6. 调度策略与公平性
6.1 常见调度策略
FCFS(First Come, First Served) 按到达时间服务。它容易解释,也能避免新请求无限插队,但会出现队头阻塞:队头长 prompt 可能阻塞后面的短请求。
优先级调度 给请求或租户设置优先级。它适合区分交互式请求、离线任务和内部维护任务,但如果没有老化机制,低优先级请求可能饥饿。
Shortest Job First 或 SRPT 优先处理预计剩余工作量较小的请求。若工作量估计准确,它通常能降低平均响应时间,但会牺牲长请求的尾延迟。
Token-level round robin 不按请求完成一次服务,而是给每个活跃请求分配有限 token 预算。例如每轮最多为每个请求服务一个 Decode token,再将剩余预算给 Prefill。这能改善 Decode 公平性,但可能降低 GPU 大矩阵操作的效率。
实际系统往往采用混合策略:
- 按租户做配额和权重;
- 租户内部按优先级或到达时间排序;
- 对长 Prefill 做 chunk;
- 对 Decode 设置最低服务份额;
- 对等待过久的请求增加老化权重;
- 预留一部分预算给交互式请求。
6.2 公平性的对象是什么
“公平”不是简单地让每个请求占用相同的 GPU 时间,因为请求的工作量不同。至少需要区分三种公平目标:
请求公平
每个请求都应在合理时间内获得服务。可以监控:
- 排队时间;
- 从进入活跃集合到首 token 的等待时间;
- 最大等待时间;
- p95、p99 TTFT。
token 公平
让不同请求或租户获得相近的 Decode token 服务份额。对租户 ,可以定义:
再与配置权重 比较。若目标是加权公平,则希望:
成本公平
长 prompt 和长输出消耗更多 GPU 资源。仅按请求数限流会让发送长上下文的租户获得更高资源消耗。成本计量至少应记录:
其中 、、 是由基准测试或计费策略确定的成本系数。
一个常用的离散公平性指标是 Jain 指数:
它的值介于 和 1 之间,越接近 1 表示服务份额越均匀。但它不能单独证明系统公平:如果所有请求都在等待,份额可能很均匀,延迟仍然很差;如果请求需求不同,均匀 token 份额也未必合理。
6.3 公平性与吞吐量不能同时无限优化
假设有两个请求:
- 请求 A:每次 Decode 的 KV Cache 很长,生成 100 token;
- 请求 B:上下文很短,生成 2 token。
如果每轮都严格给两者各一个 Decode token,公平性较好,但短请求 B 可能需要等待 A 的大量调度轮次才能完成。另一方面,如果总是优先 B,平均响应时间可能下降,但 A 的等待时间和尾延迟可能无限增加。
更极端的反例是:
- 每轮都有一个新的短请求到达;
- 调度器使用严格的短作业优先;
- 一个长请求已经等待很久。
由于每个新短请求都比长请求“更短”,长请求永远得不到服务。这是饥饿,不是公平调度。
因此,生产策略通常需要把公平性定义成约束,而不是单一目标,例如:
或:
其中 是最低服务比例。满足最低服务比例后,剩余容量再用于提高吞吐。
7. 吞吐与延迟的形式化关系
7.1 主要指标
TTFT(Time to First Token) 是从请求进入服务端到客户端收到第一个输出 token 的时间:
其中:
- :等待调度的时间;
- :处理 prompt 的时间;
- :得到首个生成 token 的执行时间;
- 流式开销包括采样、序列化和网络发送。
TPOT(Time Per Output Token) 常指后续输出 token 的平均耗时。也有系统使用 ITL(Inter-Token Latency)表示相邻 token 到达之间的间隔。两者口径可能不同,监控时必须明确是否包含网络和排队。
若输出长度为 ,近似有:
这个公式不是严格恒等式,因为不同 Decode 轮次的 batch 大小、抢占、网络阻塞和采样时间可能变化,但它能说明:TTFT 主要受 Prefill 和排队影响,长回答的总延迟则对 TPOT 更敏感。
吞吐量 至少有两种:
- request/s:每秒完成多少请求;
- token/s:每秒处理或生成多少 token。
LLM 服务更常关注 output token/s,因为一个长请求和一个短请求的资源消耗差异很大。还需要分别监控:
- Prefill token/s;
- Decode token/s;
- 总 token/s;
- 活跃序列数;
- GPU 利用率;
- KV Cache 使用率。
7.2 Little 定律的使用边界
在稳定系统中,Little 定律为:
其中:
- :系统中的平均请求数;
- :平均到达率;
- :平均响应时间。
因此,当到达率固定时,平均延迟下降通常意味着系统中平均排队和执行的请求数下降;当服务能力提升时,系统可以在相同延迟下承载更高到达率。
但不能把 GPU 利用率直接当作吞吐量。GPU 利用率 99% 可能意味着:
- GPU 正在高效处理大量 Decode;
- 一个超长 Prefill 阻塞了所有交互请求;
- 内存带宽已饱和但有效 token/s 很低;
- kernel 在处理 padding 或无效工作。
必须将利用率和 token/s、TTFT、TPOT、队列长度一起解释。
8. Prefill 优先还是 Decode 优先
8.1 Prefill 优先的效果
Prefill 优先可以快速处理新请求,降低 TTFT,尤其适合对首 token 敏感的交互式场景。
但长 Prefill 会抢占 GPU 执行预算。已有请求的 Decode 被推迟后,客户端会观察到 token 间隔突然变大,表现为:
首 token 很快
后续 token 不稳定
某些时刻长时间没有新 token
这会使 TPOT 的平均值看起来尚可,但 p95/p99 ITL 很差。
8.2 Decode 优先的效果
Decode 优先可以稳定已开始生成的请求,降低流式输出抖动,提高正在运行请求的服务连续性。
代价是新请求可能长时间拿不到 Prefill,TTFT 增大,等待队列不断增长。若输入请求持续到达,Decode 优先也可能造成 Prefill 饥饿。
8.3 更可行的混合策略
一种常见的抽象策略是:
每轮:
1. 清理完成、取消和失败请求;
2. 为已有 Decode 请求分配最低服务预算;
3. 在剩余预算中处理 Prefill chunk;
4. 如果等待时间超过阈值,提高请求的调度权重;
5. 如果 KV Cache 或显存接近上限,停止接纳新请求;
6. 执行本轮 GPU 计算;
7. 更新 token、KV Cache、指标和客户端流。
这不是某个框架的标准 API,而是调度器应满足的逻辑约束。不同推理引擎对 token budget、分页 KV Cache、chunked Prefill 和抢占的具体实现不同,不能仅凭参数名称推断其语义。
9. KV Cache 是连续批处理的资源边界
9.1 为什么请求完成后必须释放 Cache
Decode 每生成一个 token,就需要追加一组新的 。因此,KV Cache 会随输出长度增长。
如果一个服务有 个活跃请求,单 token KV Cache 占用为 ,则仅缓存部分近似为:
当请求完成、取消或失败时,如果不释放对应的缓存块,新的请求即使计算能力充足,也会因为显存不足无法接纳。
9.2 连续批处理通常需要动态内存管理
固定连续内存分配容易受到不同长度请求的影响,产生内部碎片或外部碎片。分页 KV Cache 的常见思路是:
- 将 KV Cache 切成固定大小的 block;
- 每个请求维护逻辑 token 到物理 block 的映射;
- 新 token 到来时按需分配 block;
- 请求结束后归还 block。
它的好处是不同长度请求可以共享一个物理池,而不是为每个请求分配一段必须连续的显存。
但分页不等于无限内存。调度器仍必须在接纳请求前验证:
其中 margin 用于应对临时 kernel 缓冲区、通信和碎片。只按当前已生成长度估算,而不考虑后续最大输出,可能导致请求执行到中途才 OOM。
9.3 OOM 与抢占
当新请求需要更多 KV Cache,而显存不足时,系统可以:
- 拒绝新请求;
- 延迟接纳;
- 暂停某个请求并释放其 KV Cache;
- 将状态转移到 CPU 或其他存储;
- 之后重新 Prefill 或恢复 Decode。
如果释放缓存后无法保留完整 KV 状态,就必须重新计算被抢占请求的上下文。这样可以提高整体接纳率,但会增加延迟和重复计算。
因此,“支持抢占”不能只看接口是否存在,还需要明确:
- 抢占粒度是请求、序列还是 KV block;
- 状态保存在哪里;
- 恢复时是否重新 Prefill;
- 抢占成本是否计入租户;
- 高频抢占是否会造成吞吐下降。
10. 采样、停止和流式输出也属于调度生命周期
GPU 前向通常产生 logits,采样器再根据 temperature、top-k、top-p、重复惩罚等规则选择 token。采样结果会决定请求是继续 Decode 还是结束。
请求结束条件的优先级需要明确。例如:
- 若生成 EOS,结束;
- 若匹配 stop sequence,通常需要丢弃或不发送 stop sequence 本身;
- 若达到最大输出长度,结束;
- 若客户端取消,停止继续计算并释放资源。
流式输出还存在一个系统级边界:模型已经生成 token,但网络发送可能阻塞。如果执行器继续为一个慢客户端生成 token,服务会积累未发送的 token 和额外缓存。常见处理是:
- 对每个连接设置发送缓冲上限;
- 缓冲超过阈值时暂停该请求;
- 记录客户端背压时间;
- 超时后取消请求并释放 KV Cache。
因此,TTFT 和 TPOT 应明确是“模型生成时间”还是“客户端实际收到时间”。对用户体验而言,后者更有意义;对模型性能诊断而言,前者更有意义。
11. 常见错误理解与失败表现
11.1 “连续批处理就是动态增大 batch”
不准确。核心不是 batch 数量变大,而是每个调度迭代都允许请求集合变化。若执行器仍然等待整组请求结束,或者新请求只能等完整 batch 结束,那么它仍然接近静态批处理。
11.2 “Prefill 一定比 Decode 快”
不准确。Prefill 的并行性通常更好,单位 token 吞吐可能更高;但长 prompt 会产生更大的总计算量和 KV 写入。Decode 单轮 token 少,却需要反复读取长 KV Cache,可能受内存带宽限制。
应分别测量:
- 短 prompt 和长 prompt 的 Prefill 延迟;
- 不同上下文长度下的 Decode TPOT;
- 不同并发下的 output token/s;
- 混合 Prefill/Decode 时的 ITL 尾延迟。
11.3 “GPU 利用率高,所以服务性能好”
错误。GPU 可能在执行无效 padding、超长 Prefill 或频繁小 kernel。若 GPU 利用率升高同时 TTFT、p99 ITL 和拒绝率恶化,通常说明调度或显存已成为瓶颈,而不是系统变得更高效。
11.4 “请求数限制可以防止 OOM”
不够。两个请求可能分别携带 20 和 20,000 个 token。安全准入至少应估算 prompt token、最大输出 token、KV Cache 数据类型和并发增长。
11.5 “优先级高就一定延迟低”
不一定。高优先级请求也可能被一个已经开始的长 Prefill、GPU 排队、KV Cache 不足、网络背压或 worker 故障影响。优先级只能影响调度决策,不能消除已经发射的 GPU kernel 和物理资源限制。
12. 生产诊断:从指标判断瓶颈
12.1 TTFT 高,TPOT 正常
常见原因:
- 等待队列过长;
- Prefill chunk 太大;
- Prefill 优先级过低;
- 输入 prompt 过长;
- tokenizer 或请求校验耗时;
- KV Cache 准入不足;
- 首轮 GPU kernel 启动或模型加载延迟。
验证方法是分解时间戳:
request_received
tokenization_done
queued
prefill_started
prefill_finished
first_decode_started
first_token_sent
如果 queued -> prefill_started 很长,问题在准入或调度;如果 prefill_started -> prefill_finished 很长,问题在输入长度、模型计算或 chunk 配置;如果模型已生成但 first_token_sent 很晚,则应检查采样、序列化和网络。
12.2 TTFT 正常,TPOT 或 ITL 尾延迟高
优先检查:
- 长 Prefill 是否频繁插入 Decode;
- Decode batch 是否过大;
- KV Cache 访问是否达到带宽瓶颈;
- 是否存在频繁抢占和重算;
- 慢客户端是否造成背压;
- 某些租户是否长期占用调度预算。
平均 TPOT 不能掩盖尾延迟。流式服务应重点观察 p95/p99 inter-token latency,以及单请求在连续两次 token 之间的最大间隔。
12.3 吞吐高但公平性差
典型表现:
- 总 output token/s 上升;
- 低优先级租户的最大等待时间持续上升;
- 某些长请求几乎没有 Decode 服务;
- 队列中请求年龄不断增大。
这通常说明调度器优化了吞吐,却没有限制饥饿。可以增加最大等待约束、租户最小服务份额或优先级老化,并重新测量吞吐损失,而不是只观察 GPU 利用率。
12.4 逐渐 OOM 而不是立即 OOM
这种情况常由资源释放路径缺失造成:
- 客户端断开没有触发取消;
- stop sequence 结束后未归还最后一组 KV block;
- 异常请求仍留在活跃集合;
- worker 重试时重复保留旧 Cache;
- 统计系统与执行器状态不一致。
恢复流程应包括:
- 暂停新请求接纳;
- 列出执行器认为活跃的请求;
- 对照连接、队列和 KV block 分配表;
- 释放已结束请求;
- 若状态无法可靠修复,重启 worker;
- 重启后验证模型权重、KV Cache 池和健康检查;
- 逐步恢复流量,而不是立即全量放开。
13. 模型、数据、权限与成本的统一边界
连续批处理位于模型服务层,但它不能脱离生产系统的其他约束。
输入 prompt 可能包含敏感数据,日志不应为了诊断而无条件保存完整 prompt 和生成内容。至少需要区分:
- 可记录的请求元数据;
- 脱敏后的 token 长度;
- 受权限保护的原始输入;
- 不应持久化的秘密和个人数据。
多租户调度不能只使用一个全局优先级。租户还应有:
- 最大并发请求数;
- 最大输入 token 速率;
- 最大输出 token 速率;
- KV Cache 或 GPU 时间配额;
- 单请求最大上下文和输出长度;
- 超预算后的拒绝、排队或降级策略。
模型评测也应包含服务调度因素。相同模型在不同 Prefill/Decode 策略下,可能出现不同的截断率、超时率和用户可见质量。离线只测 tokens/s,无法发现长 prompt 下 TTFT 爆炸或流式输出抖动。
成本计量应同时记录输入 token、输出 token、GPU 时间和重算成本。发生抢占重算时,如果只按最终输出 token 计费,会低估系统真实资源消耗,也会掩盖导致重算的调度问题。
14. 实现时应明确区分的三类结论
规范保证来自模型或算法定义。例如因果注意力不能读取未来 token;Decode 可以使用已经正确计算的历史 KV Cache;请求达到停止条件后不应继续生成。
常见实现来自推理引擎设计。例如迭代级连续批处理、分页 KV Cache、chunked Prefill、Prefill 与 Decode 混合调度。这些机制广泛存在,但具体参数、抢占方式、缓存布局和 kernel 行为取决于实现与版本。
经验建议来自基准测试和业务目标。例如 Prefill chunk 大小、Decode 最低服务预算、租户权重和显存安全余量都不能脱离硬件、模型和流量分布直接照搬。应使用真实 prompt 长度分布、输出长度分布和连接行为压测验证。
Transformer 的注意力基础可参考论文 Attention Is All You Need;具体模型接口、tokenizer、生成配置和版本行为应以对应的 Hugging Face Transformers 文档 及实际推理引擎文档为准。
连续批处理的本质,是在 GPU 计算迭代边界上动态管理一组具有不同生命周期的自回归序列。Prefill 决定新请求多久看到首 token,Decode 决定已有请求的流式速度,KV Cache 决定系统能同时容纳多少上下文,调度策略决定这些请求如何竞争资源,而公平性约束决定高吞吐是否会以牺牲部分请求为代价。只有把这几个部分放在同一个资源和状态模型中,吞吐、延迟与稳定性之间的取舍才可以被测量和解释。
系列导航与关联阅读
- 系列入口:AI 工程完整学习路线:从机器学习与 Transformer 到 RAG、Agent 和生产治理
- 上一篇:LLM KV Cache:内存计算、分页管理、复用、失效与容量估算
- 下一篇:推测解码:草稿模型、接受率、正确性与加速边界
官方资料
本文依据研究论文、标准组织与主流框架官方文档重新梳理;正文、示例与工程清单由 WR BLOG 编写。

评论
0 条讨论