LLM 面试备考
首页/Agent 基础设施

连续批处理与并行策略 Continuous Batching & Parallelism

所属:01-fundamentals / 06-agent-infra
面试权重:★★
来源:通用领域知识;未做一手考证
相关知识点:01-fundamentals/06-agent-infra/vllm-pagedattention.md、latency-cost.md

一句话#

连续批处理解决「批次里最长的请求决定整批何时结束」这个浪费,做法是每个 decode step 都重新组批 —— 完成的请求退出、排队的请求立刻补进来;并行策略(data / tensor / pipeline)解决「单卡装不下或单卡不够快」,三者切的东西不同、通信代价差一个量级,按模型大小与互联带宽选择。

为什么重要(面试角度)#

这是「你怎么把模型跑起来」的核心题,面试官想听:

  • 你说得清静态批处理到底浪费在哪(不是"效率低",而是 head-of-line blocking + padding)
  • 你分得清四种并行的切分对象:切数据(DP)、切层内权重(TP)、切层(PP)
  • 你知道通信模式与代价:TP 要每层 all-reduce,必须高速互联;PP 通信量小但有空泡
  • 你能给出什么时候用哪个的决策规则,而不是罗列定义

这部分和 vLLM/PagedAttention 是同一套账的两面,答的时候要串起来:PagedAttention 管显存,连续批处理管调度,并行策略管容量。

核心内容#

静态批处理的问题#

静态批处理(static batching)的流程是:攒够一批请求 → 一起前向 → 整批全部完成后统一返回,才处理下一批。

三个具体浪费:

问题 机制
head-of-line blocking / 长尾拖累 批里有 r 个长请求(如 2000 token)和 8 个短请求(如 20 token),短请求早就生成完了,但它的槽位被占着空转,直到长请求结束整批才释放
padding 浪费 为了对齐序列长度需要 padding,padding 的位置计算了但没意义
吞吐低、延迟长 GPU 在批次尾部大量空闲;新请求必须等整批结束才能进来,排队延迟高

一句话:静态批处理的利用率由"最长的那条"决定,而请求长度分布通常长尾很重,所以这个决定往往是坏的。

连续批处理(continuous batching)#

也叫 iteration-level scheduling:调度粒度从"整个请求的生命周期"降到"一个 decode step"。

每个 step 结束时:
  1. 检测哪些序列已生成结束符 / 达到 max_tokens  → 移出批次,释放显存
  2. 检测队列里是否有等待的请求                → 插入空闲槽位
  3. 组新批次,进入下一步 decode

效果:

  • 没有 head-of-line blocking:短请求做完立刻走,不陪长请求空转。
  • GPU 利用率稳定:批次始终尽量填满,吞吐显著上升。
  • 排队延迟下降:新请求不用等整批结束。

代价与注意点:

  • 需要配合分页的 KV Cache(PagedAttention)才能高效地动态分配/释放每个序列的显存 —— 否则动态进出会造成严重碎片(→ 见 vllm-pagedattention.md)。这两者是配套设计,答的时候最好一起讲。
  • 对个体延迟不总是更好:一个请求可能被更大的批次拖慢单步速度(TPOT 略升),换来的是吞吐和整体排队时间下降。吞吐与延迟的取舍依然存在(→ 见 latency-cost.md)。
  • 调度策略本身有讲究:先来先服务 vs 优先级 vs 按剩余长度;prefill 与 decode 混批时可能互相干扰(长 prompt 的 prefill 会占住一步),所以有 chunked prefill(把长 prompt 的 prefill 切成分片,与 decode 交错执行)来稳定延迟。

并行策略:切什么、付什么代价#

四种并行切的维度完全不同,这是最容易记混的地方:

策略 切什么 通信模式 通信量 主要目的 / 适用
Data Parallelism (DP) 数据(每个副本持完整模型,各跑一批不同数据) 训练时梯度 all-reduce;推理时无通信,各副本独立 + 负载均衡 训练:大(每步同步梯度);推理:几乎为零 训练时扩大总 batch;推理时用多副本抬吞吐(副本间不需要通信)
Tensor Parallelism (TP) 层内权重(把权重矩阵按列/行切到多卡,每卡算一部分,再合并) 每层都要 all-reduce / all-gather 大,与层数同阶 单卡放不下权重,或想降低单层的单卡计算压力
Pipeline Parallelism (PP) 层间(把不同层放到不同 stage,数据以 micro-batch 流过) 只在 stage 边界传激活值 小 跨机扩展;显存不够且不想付 TP 的通信开销
(训练专属)ZeRO 式分片 优化器状态 / 梯度 / 参数 按需 all-gather / reduce-scatter 中–大 训练时把"每卡存全量副本"的冗余压掉,能训更大模型。⚠️ 但它不是一条可乘的并行轴(见下方说明)

必须说清的三个点:

  1. TP 的通信必须在快互联上。因为每层都有 all-reduce,带宽不够就会把计算优势吃光。所以 TP 通常限制在单机内(NVLink 那类高速互联),跨机用 TP 一般得不偿失。
  2. PP 有空泡(bubble)。流水线的第一个和最后一个 stage 会等待,用 micro-batch 填充可以摊薄空泡,但流水线越深、micro-batch 越少,空泡占比越大。PP 的好处是通信量小、天然适合跨机。
  3. DP 是最省事的扩展方式。推理场景尤其简单:复制 N 个完整副本,前面加负载均衡,吞吐近似线性提升(只要上游不成为瓶颈),代价是显存要放得下 N 份模型 —— 这就是为什么 DP 在推理里常和量化搭配。

怎么组合(这是面试最想听的部分)#

真实的部署是这几种的乘积:

总卡数 = TP × PP × DP

典型的分层规则:

  • 单卡放得下权重 → 直接用 DP(多副本 + 负载均衡)。最简单、无通信开销,先用满这条路。
  • 单卡放不下,但单机能放下 → 在机内用 TP(吃 NVLink 带宽)。例如把模型切到同机的若干张卡上。
  • 单机也放不下 → PP 跨机(通信量小,能容忍较慢的互联),机上再叠 TP。
  • 训练 → 优先级通常是:先用 DP + ZeRO 式分片压冗余,再叠 TP(层内),再叠 PP(跨机),最后用梯度累积凑等效 batch。

一句话判据:按「显存缺口有多大」决定要不要切权重(TP)、按「跨不跨机」决定切层(PP)、按「还要不要更大的总吞吐/batch」决定复制副本(DP)。

⚠️ ZeRO / FSDP 不能乘进上面这个公式里 —— 它不是一条并行轴。

上面那张表把 ZeRO 与 TP/PP/DP 并列,这是容易误导的排布。关键区别:TP/PP/DP 改变的是「有多少份计算在并行」,ZeRO 改变的是「每一份里状态怎么存」。所以:

  • ZeRO 是数据并行副本内部的状态分片 —— 它是挂在 DP 底下的一层,不增加卡数,也不改变 总卡数 = TP × PP × DP。
  • 它切的是训练状态(优化器状态 / 梯度 / 参数),属于训练状态分片;而 TP/PP 切的是执行激活。这是两条不同的并行轴,论文里把它画成独立层级,正是为了说明这一点。
  • 面试里常被问「ZeRO 和 TP 有什么区别」—— 一句话分界:TP 是把一层拆到多卡并行算,ZeRO 是让每张卡少存一点冗余的副本。前者解决算不动,后者解决存不下。

另外仓库此前完全没提长序列训练专属的两条轴:序列并行(SP)与上下文并行(CP) —— 它们切的是序列长度维度,在长上下文训练里是必需的,值得知道有这条路。

常见坑#

  • KV Cache 也要按并行的切法分摊:TP 会把 KV 头也切到各卡,所以每卡的 KV 占用约变成 1/TP —— 这常被忽略,但它是 TP 能提高并发的另一个原因。

    ⚠️ 但这条在 EP(专家并行)场景下会低估显存:注意力层与 KV 会在各个 EP 组之间各复制一份。举例:一个 TP2 × EP4 的实例里,一条 8192 token 的上下文在整个实例内占 约 5.875 GiB,是单份逻辑 KV 的四倍(四个 EP 组各一份)。所以「每卡 KV ≈ 总 KV / TP」这条只在纯 TP 下成立;同时用 EP 时必须把 EP 的复制倍数乘回去,否则容量估算会差好几倍。

  • PP 的 micro-batch 数要够:太少 → 空泡大;太多 → 单个 micro-batch 太小、效率低。需要调。
  • DP 副本之间要保证一致性:同一用户的连续请求如果被负载均衡到不同副本,且服务端有本地状态(会话状态、前缀缓存),会出问题 —— 需要粘性路由或外置状态。
  • 并行度不是越大越好:TP/PP 都有通信与空泡开销,超过某个点后增大并行度反而降低单请求速度(虽然总容量变大)。
  • 推理与训练的并行诉求不同:训练要为反向传播保留激活,显存压力大得多;推理没有反向,显存压力主要在权重 + KV。

常见面试问法#

Q:静态批处理和连续批处理有什么区别? A:静态批处理攒一批一起跑、整批完成后才释放并接收下一批,短请求要陪长请求空转(head-of-line blocking),还有 padding 浪费,GPU 利用率由最长序列决定。连续批处理把调度粒度降到单个 decode step:每步检测完成的序列退出、排队的请求补入,批次始终尽量填满。结果是没有长尾拖累、吞吐和排队延迟都改善;代价是需要分页的 KV Cache 支撑动态分配,且个体延迟仍会受批次大小影响。

Q:TP / PP / DP 分别切什么?什么时候用哪个? A:DP 切数据(每张卡一份完整模型,各跑不同数据,推理时无通信);TP 切层内权重(每层 all-reduce,通信量大,需要高速互联,通常限制在单机内);PP 切层间(不同层放不同 stage,只在边界传激活,通信量小,可跨机,但有空泡)。选择规则:单卡放得下就用 DP(最简单、无通信);单机放得下但单卡放不下用 TP;单机也放不下用 PP 跨机。

Q:为什么张量并行一般只在单机内做? A:因为 TP 在每一层都有 all-reduce/all-gather,通信量与层数同阶。如果这些通信走的是跨机网络而不是机内高速互联(如 NVLink),带宽与延迟会显著劣化,通信时间很可能超过切分带来的计算收益,得不偿失。所以 TP 通常限制在单机(几张卡)内,跨机扩展改用通信量小得多的 PP。

Q:流水线并行的"空泡"是什么?怎么缓解? A:PP 把层分到不同 stage,第一个和最后一个 stage 必然有等待(填充流水线和排空流水线的时间),这段时间部分卡是空闲的,就是空泡。缓解方式是用 micro-batch 流水填充以提高填充度、增加 micro-batch 数量摊薄首尾开销,以及平衡各 stage 的计算量。代价是 micro-batch 太多时单个 batch 效率下降,通信/调度开销占比上升,需要权衡。

Q:推理服务要扩吞吐,加机器和加卡分别怎么做? A:最简单的是加副本(DP):多起几个完整模型实例,前置负载均衡 —— 无通信开销,吞吐近似线性提升,前提是显存放得下每份模型(放不下就配合量化)。如果单个请求就要多卡(单卡放不下模型),则优先机内 TP、跨机用 PP,然后在这些之上再叠 DP 副本。要注意的是:DP 副本若带本地状态(会话、前缀缓存),需要粘性路由或把状态外置,否则会话会被打散。

易错点#

  • ❌ 说静态批处理"效率低"却不点出机制 —— 要说清 head-of-line blocking:短请求被长请求拖住、槽位空转。
  • ❌ 把连续批处理和 PagedAttention 当成一回事。一个是调度(每步重组批次),一个是显存管理(分页 KV),二者互补,独立有价值。
  • ❌ 混淆 TP 和 PP:TP 切层内权重(每层通信、量级大),PP 切层(边界通信、量级小)。说反了很尴尬。
  • ❌ 说 DP 在推理时也需要梯度 all-reduce —— 那是训练。推理的 DP 就是多副本 + 负载均衡,几乎没有通信。
  • ❌ 认为并行度越大越快。TP/PP 有通信与空泡开销,超过某点后单请求变慢,只是总容量变大。
  • ❌ 忽略 PP 的空泡,或忽略 micro-batch 数量对空泡的影响。
  • ❌ 忘了 KV Cache 会随 TP 一起被切分,导致显存估算错误。
  • ❌ 把训练与推理的并行诉求混为一谈(训练要为反向传播留激活,显存压力大得多)。

自测#

1. 一批里有 1 个 2000-token 的长请求和 8 个 20-token 的短请求。静态批处理浪费在哪?连续批处理怎么解决?

答案

静态批处理把整批一起跑、整批完成后才释放槽位并接收下一批。那 8 个短请求在 20 步左右就生成完了,但它们的槽位被占用空转直到长请求结束(head-of-line blocking),GPU 在批次尾部大量空闲;此外还有序列对齐带来的 padding 浪费。连续批处理把调度粒度降到单个 decode step:短请求一生成完就退出并释放显存,队列里的新请求立刻补入空出的槽位,批次始终尽量填满。结果是吞吐上升、排队延迟下降;这需要分页 KV Cache 支撑动态分配与释放。

2. TP 和 PP 分别切什么?为什么 TP 对互联带宽敏感而 PP 相对不敏感?

答案

TP 切层内权重:把每层的权重矩阵按列/行切到多张卡,每张卡算一部分再合并,因此每一层都需要 all-reduce/all-gather,通信量与层数同阶,且延迟直接叠在每步 decode 上 → 必须走机内高速互联(NVLink 那类),否则通信会吃掉计算收益。PP 切层间:把不同层放到不同 stage,只在 stage 边界传递激活值,通信次数与 stage 数同阶而非层数,量级小得多 → 可以容忍较慢的跨机互联。代价是 PP 有流水线空泡,需要 micro-batch 填充。

3. 单卡放不下一个 70B 模型(以 FP16 估算),但你有 8 卡单机 + 多机。你怎么编排 TP/PP/DP?

答案

先量化:70B FP16 光权重就需要远超单卡显存,必须切分。基本思路:① 优先在机内用 TP 把权重切到 8 张卡(吃 NVLink,把每卡的权重占用降到约 1/8,同时 KV 也按 TP 分摊);② 如果 8 卡仍放不下(或要留足 KV 空间给高并发/长上下文),就上 PP 跨机,把层分到不同机器;③ 在这之上再叠 DP:多起几组(TP×PP)的副本做负载均衡来提高总吞吐。实际选择要看互联条件、上下文长度与并发目标:上下文要求高就得多留 KV 显存(考虑量化权重与 KV 量化);吞吐要求高就多上 DP 副本。另外要注意 DP 副本间若有本地状态(前缀缓存、会话)需要粘性路由或外置状态。

4. 为什么说「并行了就会变快」是错的?

答案

并行切分会引入额外开销:TP 每层都要 all-reduce,通信量与层数同阶,互联不够快时通信时间可能超过计算收益;PP 有流水线空泡(首尾填充/排空,micro-batch 少时空泡占比大),还有 stage 间激活传递的通信与调度成本;DP 在训练时需要同步梯度。所以增大并行度往往增大系统总容量(总吞吐),但单请求的延迟可能反而变差。并行策略是"用通信换容量",不是无代价的加速,要按瓶颈在哪(显存还是延迟)来选。

5. 为什么说连续批处理要和 PagedAttention 一起用?

答案

连续批处理每步都让序列动态进出,意味着 KV Cache 的显存要频繁地分配与释放、且大小随序列增长而变化。如果 KV Cache 按"预分配连续一大块"的传统方式管理,这种频繁的变长分配释放会造成严重的内部与外部碎片,显存利用率急剧下降,动态调度的收益被抵消。PagedAttention 把 KV 切成固定大小的 block 按需分配、用 block table 映射,让动态进出几乎不产生碎片。所以两者是配套设计:连续批处理提供吞吐的调度空间,分页 KV Cache 提供实现它的显存基础。