系统篇

08|推理服务:调度、吞吐、延迟和并行

系统篇约 5.8 千字1 组分步图解
本章目录

本章预备知识

服务性能不只有每秒生成多少Token,还包含排队、首字、工具以及恢复时间。并行可能省墙钟时间,也可能增加通信;吞吐更高不保证每一个人的响应更快。

本章各题列出所需基础。可先完成相应基础课,也可通过自检确认掌握程度。

进入深读前的小检查:把串行任务2+3+4+1改成2+max(3,4)+1,再加入4的协调开销,亲自看并行如何从有利变成不利。

术语带虚线时可点击速查。公式中的字母先看定义;不要用猜缩写替代推导。

模型前向只是服务的一部分。一个请求在队列等待多久、与谁一起执行、KV是否被抢占、输出何时返回,都会决定用户感受到的速度。

沿着对象与状态理解本章

先把单个请求的等待时间和整个服务的吞吐分开。下图用相同服务槽位、相同请求长度比较静态组批与连续组批;每步耗时被明确理想化,后面的真实调度仍要加入prefill、队列与设备测量。

下方图解的固定输入在“输入与适用条件”中声明。它用于解释本章机制,不与本章其他独立例子的数值暗中接续。

动态图解 · O06

连续组批:有座位空出来,就接下一位

为什么设备更忙,不代表每条请求都更快?

固定批次像一车人必须一起离站;连续组批允许有人结束就补入新请求。这样减少空转,有机会提高吞吐。但已有请求的关键步骤未必缩短,新请求的预处理、排队与竞争还可能增加部分延迟。

槽位简化表示一次迭代能同时处理的请求位置。
Prefill新请求的完整已知输入处理阶段。
公平性避免某类请求长期等不到资源。

输入与适用条件:两槽位、三请求,A/B/C分别需2/4/2个等时decode步骤。忽略prefill和通信,只说明调度机制。

1 / 4 · 普通批次等待整体结束

第1步,共4步

窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。

连续组批:有座位空出来,就接下一位:普通批次等待整体结束固定批次:6步步1AB步2A✓B步3空闲B步4空闲B✓步5C空闲步6C✓空闲连续组批:4步步1AB步2A✓B步3CB步4C✓B✓每步耗时相同的简化假设;✓表示该步结束时请求完成。

两槽位、请求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请求的资源成本很不同。还要防止短任务一直插队使长任务饥饿。

运行示例 · Q029

请求B结束后,下一轮怎样把空位交给C

输入与条件。教学调度器最多同时放2个请求。A还要生成3个Token,B还要1个,C在队列里。每个decode轮每条活跃请求最多生成1个Token。

步骤与对象内部动作中间结果 / 新状态
1第1轮处理A和BA剩2个,B生成完并退出。
2轮间调度释放B的请求槽位和不再需要的缓存C可以进入,不必等A结束。
3第2轮处理A及已完成prefill的CA剩1个,C取得自己的第1个输出。
4长输入反例若C的prefill一次占用很长时间A的下一个输出可能等待,形成prefill对decode的干扰。
5切块选择把C的prefill分段,并在合适边界调度可控制等待,但会引入调度与吞吐取舍。

把过程连起来。连续批处理更新的是轮间活跃请求集合,不能把不同请求的历史上下文混到一起。

边界与反例。轮次示例省略硬件耗时与prefill实现,不能直接据此计算真实吞吐。切块与优先级不是无条件收益。

依据与适用范围:[CACHE] [PAGED]。示例输入和场景由本书明确设定。

追问:吞吐高是否意味着单个用户更快?

不一定。更大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;即使理论算力翻倍,也不一定改善这一项。这个例子是单位推算,不是任何产品实测。

运行示例 · Q030

首字慢、后续快和总任务慢可以同时发生

输入与条件。假设一次请求排队40ms、prefill 60ms,随后第1个Token还需20ms,后续每个Token间隔20ms,共输出4个Token。

步骤与对象内部动作中间结果 / 新状态
1首Token时刻40+60+20TTFT=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,具体协议取决于实现。

运行示例 · Q031

草稿错了仍可保持目标分布:三种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.60.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。若网络慢、上下文短或请求量小,分离收益可能被传输与排队抵消。

运行示例 · Q032

同样两张设备,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]

记录保存在本机浏览器。

输入关键词即可检索。

↑ ↓ 选择 Enter 打开Esc 关闭 · 仅本地搜索

阅读设置

阅读设置仅保存在本机浏览器,不上传任何数据。