08|推理服务:调度、吞吐、延迟和并行
本章目录
本章预备知识
服务性能不只有每秒生成多少Token,还包含排队、首字、工具以及恢复时间。并行可能省墙钟时间,也可能增加通信;吞吐更高不保证每一个人的响应更快。
本章各题列出所需基础。可先完成相应基础课,也可通过自检确认掌握程度。
进入深读前的小检查:把串行任务2+3+4+1改成2+max(3,4)+1,再加入4的协调开销,亲自看并行如何从有利变成不利。
术语带虚线时可点击速查。公式中的字母先看定义;不要用猜缩写替代推导。
模型前向只是服务的一部分。一个请求在队列等待多久、与谁一起执行、KV是否被抢占、输出何时返回,都会决定用户感受到的速度。
沿着对象与状态理解本章
先把单个请求的等待时间和整个服务的吞吐分开。下图用相同服务槽位、相同请求长度比较静态组批与连续组批;每步耗时被明确理想化,后面的真实调度仍要加入prefill、队列与设备测量。
下方图解的固定输入在“输入与适用条件”中声明。它用于解释本章机制,不与本章其他独立例子的数值暗中接续。
连续组批:有座位空出来,就接下一位
为什么设备更忙,不代表每条请求都更快?
固定批次像一车人必须一起离站;连续组批允许有人结束就补入新请求。这样减少空转,有机会提高吞吐。但已有请求的关键步骤未必缩短,新请求的预处理、排队与竞争还可能增加部分延迟。
输入与适用条件:两槽位、三请求,A/B/C分别需2/4/2个等时decode步骤。忽略prefill和通信,只说明调度机制。
1 / 4 · 普通批次等待整体结束
窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。
两槽位、请求A需2步、B需4步、C需2步且等待。普通批次中A完成后空出的位置仍要等B结束,再处理C。
A2步;B4步;C2步;槽位2默认静止。用“下一步”看完解释再继续;播放到最后一步自动停止。键盘聚焦播放器后,可用左右键逐步查看、空格播放/暂停。图中颜色辅助区分,含义同时用文字注明。
为什么需要
不同请求长度不同,固定批次会浪费提前空出的计算位置。
怎样实现
在迭代边界移除完成请求、纳入候选,并用预算管理prefill/decode竞争。
可能的代价与误解
吞吐提升可能伴随首Token或长尾延迟上升,不能只看平均速度。
如何判断是否做对
同负载测吞吐、TTFT、逐Token延迟和尾延迟;单独报告排队。
Q029|Continuous Batching与普通Batch有什么区别?Prefill为什么会干扰Decode?
核心回答
普通静态batch常等待一组请求全部结束再换下一组;continuous batching在生成迭代之间动态移出完成请求、加入新请求,提高设备利用率。长Prefill计算与短Decode迭代争用设备,需要调度策略权衡首Token延迟、后续Token间隔和总体吞吐。[PAGED]
跟踪四个请求
A需要生成100Token,B只需5Token。静态batch中B很快完成,但A未结束时可能占着固定batch位置。迭代级调度在B结束后让C进入;C先完成Prefill,再参与后续Decode。若D携带超长Prompt突然进入,一次性Prefill可能占用较长GPU时间,使A/C的Token输出停顿。
Chunked prefill把D的输入分块处理,使调度器可在块间安排已有请求Decode。这降低某些阻塞,但D建立完整前缀需要更多轮调度,调度和缓存管理也更复杂。具体引擎怎样融合prefill与decode、每轮Token预算多少,是版本和配置行为,而非所有服务统一算法。
调度器常同时受最大batch Token数、KV容量、队列优先级和请求长度约束。只按请求个数限流不够:一个100Token请求和一个100kToken请求的资源成本很不同。还要防止短任务一直插队使长任务饥饿。
请求B结束后,下一轮怎样把空位交给C
输入与条件。教学调度器最多同时放2个请求。A还要生成3个Token,B还要1个,C在队列里。每个decode轮每条活跃请求最多生成1个Token。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1第1轮 | 处理A和B | A剩2个,B生成完并退出。 |
| 2轮间调度 | 释放B的请求槽位和不再需要的缓存 | C可以进入,不必等A结束。 |
| 3第2轮 | 处理A及已完成prefill的C | A剩1个,C取得自己的第1个输出。 |
| 4长输入反例 | 若C的prefill一次占用很长时间 | A的下一个输出可能等待,形成prefill对decode的干扰。 |
| 5切块选择 | 把C的prefill分段,并在合适边界调度 | 可控制等待,但会引入调度与吞吐取舍。 |
把过程连起来。连续批处理更新的是轮间活跃请求集合,不能把不同请求的历史上下文混到一起。
边界与反例。轮次示例省略硬件耗时与prefill实现,不能直接据此计算真实吞吐。切块与优先级不是无条件收益。
追问:吞吐高是否意味着单个用户更快?
不一定。更大batch可能提高总Token/s,但增加排队与每步等待。在线交互通常重视TTFT和Token间隔的尾分位数;离线批处理更重视单位成本和总吞吐。必须先确定服务目标,再选择调度参数。
追问:能否一次把所有请求Prefill完再Decode?
这样可能减少某些调度复杂度,却会让已准备好的请求等待后续长输入。相反,完全优先Decode又会让新请求长期没有首Token。调度是在有限算力与KV容量下分配等待,不存在对所有负载都最优的一条固定规则。
追问:为什么客户端重试可能让服务更慢?
若超时请求在服务器仍运行,客户端重试可能创建重复任务,增加队列和KV负担,再造成更多超时。需要明确请求身份、取消语义和服务端去重能力,使用退避与总截止时间。重试不是免费的容错按钮。
Q030|TTFT、TPOT、端到端延迟怎样拆?怎样用Roofline定位瓶颈?
核心回答
TTFT是请求到首个输出Token的时间,包含排队和Prefill等;TPOT描述后续Token生成平均或分位延迟;任务总耗时还包含检索、工具和多轮调用。Roofline用计算量与数据搬运量估计计算受限还是带宽受限,但只是上界模型,不代替实测。
写出一条请求的时间账
用户任务时间 = 上下文准备 + 检索/工具 + 网络与排队
+ Prefill + 各Decode步 + 后处理与验证
在单次生成中,可粗略写T ≈ T_queue + T_prefill + M*t_decode,但实际decode步耗时会随长度、batch变化,并可能夹杂抢占。首Token可能来自Prefill的最终logits,不应重复多算一个完整Decode。流式传输改善感知等待,但不会自动减少总计算。
Roofline的简化估算:给F次浮点操作、R字节内存传输、设备有效算力C和带宽B,则时间不低于max(F/C,R/B)。如果算术强度F/R小于硬件C/B,更可能受带宽约束。Prefill大矩阵乘通常更能利用算力;低batch decode常有大量权重/KV读取,可能更受带宽影响。实际还受通信、调度、kernel启动和缓存命中影响。
教学例:某步需读取20GB数据,有效带宽1TB/s,仅搬运下界就是约20ms;即使理论算力翻倍,也不一定改善这一项。这个例子是单位推算,不是任何产品实测。
首字慢、后续快和总任务慢可以同时发生
输入与条件。假设一次请求排队40ms、prefill 60ms,随后第1个Token还需20ms,后续每个Token间隔20ms,共输出4个Token。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1首Token时刻 | 40+60+20 | TTFT=120ms。 |
| 2全部完成 | 120+3×20 | 总生成延迟180ms。 |
| 3每步间隔 | 只看后续3个间隔 | TPOT=20ms,不等于180/4。 |
| 4工具加入 | 生成结束后还等待数据库800ms | 用户任务延迟至少980ms,改善decode未必最有效。 |
| 5定位反例 | TTFT从120变500,后续间隔仍20 | 优先分开检查排队和prefill,不把所有延迟归因于模型输出速度。 |
把过程连起来。先确定测量起止点,再比较延迟;服务时间、排队时间、工具时间不能相互替代。
边界与反例。数值是确定的教学时间线,不是硬件基准。不同平台TTFT计时起点可能包含不同网络环节。
依据与适用范围:[DERIVE]。示例输入和场景由本书明确设定。
追问:减少输入Token一定比减少输出Token更省时间吗?
不一定。输入可在Prefill中并行处理,输出存在自回归依赖;短输出节省多步decode常很有效。但超长输入带来的Prefill与后续KV读取也可能占主导。还要考虑缓存命中:删改前缀可能使本可复用的内容重算。优化应对照实际trace,而不是只数总Token。
追问:为什么平均延迟下降,用户仍抱怨?
可能P95/P99恶化,长任务被抢占,或者工具偶发超时。平均值掩盖尾部和不同请求类型。应分上下文长度、输出长度、工具类型、缓存命中、租户/地区等切片,但注意样本量和隐私。
追问:哪些优化只是把工作移到了别处?
离线预计算、缓存预热、额外压缩模型、并行检索都可能降低交互等待,但增加后台计算、存储或其他请求负担。应统计成功任务总成本与资源占用,而不只看某个局部span变短。
Q031|Speculative Decoding为什么能更快,还能保持目标分布?
核心回答
先由便宜草稿模型提出多个候选Token,再由目标模型一次验证这些位置;接受足够多草稿时可减少目标模型的串行调用。标准随机推测解码使用接受概率和拒绝后的残差采样,使最终分布与目标采样分布一致,而不是盲目采用草稿。[SPECULATIVE]
从单步分布推导正确性
目标分布p,草稿分布q,先采样x~q,以min(1,p(x)/q(x))接受。于是接受路径落到x的概率为min(p(x),q(x))。剩余目标质量为max(p(x)−q(x),0);拒绝时从这一残差归一化分布采样,就补回目标缺口。
例:p=[0.6,0.3,0.1],q=[0.4,0.4,0.2],三个候选接受率分别为[1,0.75,0.5],接受质量为[0.4,0.3,0.1],总拒绝概率0.2;残差全在第一个候选,所以最终仍是[0.6,0.3,0.1]。本包计算脚本核对了这个等式。
多Token情况下,草稿按自己的已生成前缀提出K个Token,目标模型并行算这些条件位置的分布。按顺序检查,遇到第一次拒绝后不能继续沿用后面的草稿,因为条件前缀已经改变。必须回滚对应缓存并从修正位置继续。全部接受时还可利用目标分布生成额外Token,具体协议取决于实现。
草稿错了仍可保持目标分布:三种Token逐项对账
输入与条件。目标分布p=[0.6,0.3,0.1],草稿分布q=[0.2,0.5,0.3]。这里只研究一个草稿位置,不把一整段并行验证省略成同一个公式。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1接收概率 | 分别计算min(1,p/q) | 得到[1,0.6,1/3]。 |
| 2直接接收质量 | q乘各自接收概率 | 得到[0.2,0.3,0.1],合计0.6。 |
| 3拒绝总质量 | 1−0.6 | 0.4。 |
| 4拒绝后重采样 | 按max(p−q,0)归一化 | 只有第1项有0.4,重采样分布为[1,0,0]。 |
| 5最终质量 | 直接接收质量加0.4乘重采样分布 | 得到[0.6,0.3,0.1],恰好还原p。 |
| 6错误对照 | 拒绝后直接从p采样 | 结果为[0.44,0.42,0.14],反而不等于p。 |
把过程连起来。分布保持来自“接收规则+残差分布”共同成立,不是只要有大模型验收就自然成立。
边界与反例。这是单位置精确算例,不含草稿生成开销、全段验收调度和加速收益;q为0的分量需要正确处理支持集。
依据与适用范围:[SPEC]。示例输入和场景由本书明确设定。
追问:为什么草稿越长不一定越快?
草稿生成本身有成本,后面的Token更可能在前面拒绝后作废;验证长块也消耗计算和KV。最佳长度取决于接受率、草稿/目标速度和硬件。任务分布变化可能让一个原本有效的草稿模型失去优势。
追问:温度、Top-p应在哪个分布上处理?
接受与残差公式必须对应实际希望采样的目标分布,以及实际草稿分布。若目标已应用Top-p但比值还使用原始softmax,就可能破坏分布一致性。贪心推测有相应更简单的匹配规则,不能把随机采样公式和贪心验证随意混用。
追问:保持分布为什么不保证逐字输出与普通生成一样?
相同分布不意味着用同一随机种子得到同一路径。随机数消耗顺序、浮点和实现可能不同。正确承诺是算法条件下的分布等价,而不是任意设置下字符串逐bit一致,更不是答案事实完全正确。
Q032|DP、TP、PP、ZeRO和Prefill/Decode分离分别切什么?
核心回答
Data Parallel复制模型处理不同数据;Tensor Parallel拆层内张量计算;Pipeline Parallel按层分段;ZeRO类技术拆分训练状态冗余;Prefill/Decode分离让不同资源承担两种推理阶段。它们作用的对象和通信模式不同,不应只背“把模型分到多卡”。
用一次前向说明TP与PP
一个大线性层Y=XW可以按W的输出列拆到多个设备,各卡计算部分输出;后续层若需要完整Y,就要通信或配套分片布局。另一种按输入维度拆分会产生多个部分和,需要归约。attention头、FFN中间维度如何切分决定何时all-reduce/all-gather,而非所有层都把结果简单拼接即可。
PP把前几层放设备A、后几层放B。一个micro-batch先经过A,再把激活送到B;多个micro-batch可形成流水线,但启动/结束存在气泡。阶段计算量不平衡会让快设备等待慢设备;推理单条自回归序列还需在每Token重新穿过各阶段。
DP适合有多请求/多样本时并行处理;若单个模型装不下一卡,纯DP并不能解决。训练时各副本需同步梯度;推理时独立副本通常可独立服务请求。ZeRO/FSDP把优化器、梯度乃至参数分片,减少训练副本冗余,但在计算需要时要通信获取相应部分。
Prefill/Decode分离在Prefill设备算完后把KV等必要状态送给Decode设备。资源可分别针对吞吐/带宽优化,但KV传输可能很大:第07章例子一条32k序列就是4GiB。若网络慢、上下文短或请求量小,分离收益可能被传输与排队抵消。
同样两张设备,DP、TP与PP各自切开什么
输入与条件。一个示意网络有4层,每层权重占100MB;不计算激活、梯度和优化器。两条输入A、B,两张设备。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1数据并行DP | 两张设备各放全部4层 | 权重各400MB,一张处理A,一张处理B;训练时还需同步梯度。 |
| 2张量并行TP | 每层的矩阵计算分给两张设备 | 理想均分时权重各200MB,但层内要交换或归约中间结果。 |
| 3流水线并行PP | 设备0放第1—2层,设备1放第3—4层 | 权重各200MB,层间传激活,微批安排不佳会有空闲气泡。 |
| 4ZeRO区别 | 切分优化器、梯度或参数的副本 | 不等于简单把4层一分为二,不同阶段通信不同。 |
| 5推理解耦 | prefill和decode放在不同执行资源 | 还必须转移或共享正确的KV状态,不能只移交一个请求编号。 |
把过程连起来。并行名字背后是切数据、切矩阵、切层或切状态;画出每张设备实际持有什么才能估算收益。
边界与反例。权重均分只作账本示例;实际模型有不可均分模块、通信拓扑、复制与额外缓存。
依据与适用范围:[DERIVE]。示例输入和场景由本书明确设定。
追问:更多GPU为什么反而慢?
单卡计算变少,但同步、传输和调度增加;小模型或小batch可能通信占主导。跨节点与同机互联带宽差别很大。需要比较真实任务的加速比和每成功任务成本,而不是用GPU数量当能力指标。
追问:TP度改变为什么可能影响复现?
分片与归约改变浮点加法顺序,微小logit变化可能改变临界Token选择。还可能使用不同内核。测正确性应区分数值容差、生成分布、任务结果三层,不宜要求所有并行拓扑逐字完全一致。
追问:应用工程师需要实现这些内核吗?
不一定,但需要知道配置影响哪个瓶颈。例如API延迟由远程工具占主导,研究TP不会解决;本地部署KV耗尽,就应看上下文、并发和状态管理。深入原理的目的,是能正确定位边界而非把所有层都自己重写。
本章分布式训练与并行部分的延伸依据:PyTorch张量并行说明与ZeRO原始论文。[TP][ZERO]