管线并行的气泡:微批切分、气泡率与设备利用率

在一组跨机部署的 **** 集群上(每机 8 卡,单卡 64GB HBM,机内卡间互联 392 GB/s,跨机走 RDMA 以太网),一个模型单机放不下,按层切成若干段放到不同机器上,把 --pipeline-parallel-size 从 2 提到 4、8。显存压力缓解、服务能起来,但端到端吞吐没有随档位线性上升,首 token 延迟反而随档位抬高;设备利用率曲线呈周期性塌陷,几个 rank 轮流失闲,另一些 rank 持续高位。这类形态是管线气泡:微批在管线首尾填充与排空期间,多数阶段没有可算的活。

微批切分与气泡的产生

管线并行把模型的层切成 S 段,每段落在一个设备上,S 等于 pipeline-parallel-size。一段完整的批进入管线后,逐段推进:段 0 算完把激活传给段 1,段 1 算的同时段 0 已经在处理下一批。

为了让各段同时有可计算的批,一个批被切成 M 个微批,依次进入管线。填充阶段只有段 0 在算,后面的段在等第一个微批推进到它们;排空阶段前面的段已经算完、后面的段还在收尾。首尾这两段里,部分设备空闲,空闲部分就是气泡。段数越多,填充与排空各占的步数越多,气泡在总时间里的占比越高。

微批数 M 是可以调的:M 越大,每个微批越小,填充与排空的固定开销被摊得越薄,气泡占比随之下降。

与微批数的关系与气泡率估算

气泡率可按下面的估算式取值:

气泡率 ≈ (pipeline_parallel_size - 1) / (num_ubatches + pipeline_parallel_size - 1)

代入几组取值(估算,非实测):

pipeline_parallel_size num_ubatches 气泡率估算
4 4 约 43%
4 8 约 27%
4 32 约 8.6%
8 32 约 18%

读法:固定段数时,微批数翻倍,气泡率明显收窄;固定微批数时,段数翻倍,气泡率上升。这也是「PP 调大后利用率填不满」的第一层原因。

微批数的来源随框架不同。参数名上,vLLM 用 --pipeline-parallel-size,SGLang 接受 --pipeline-parallel-size,短名 --pp-size;微批数可由 --ubatch-size 直接设定(默认 0)。核对时以启动日志里实际生效的段数与每步处理的微批数为准。

组合顺序:张量并行在内、管线并行在外

两类并行同时用时,常规组合是张量并行放在机内、管线并行放到跨机。原因在通信频率:

rank 排列上表现为:张量并行是最内层,tensor_parallel_size 个 rank 落在同一机内的互连域里;管线并行是外层,跨机扩展。调度侧的拓扑约束也按这个口径落位——小规模张量并行作业落进一个机内互连域,大规模管线并行作业落进一个机架。把顺序反过来(张量并行跨机)会让每层 all-reduce 走低速链路,拐点大幅前移。

现象

管线气泡以几种可观测形态出现:

前两条指向气泡与管线深度,后两条指向「占用」与「有效利用」两套口径的落差。资源占用率回答机器是否被占住,有效利用率回答计算单元是否在高强度计算,两者不是一回事。

触发条件

定位步骤

  1. 固定工况:同机、同卡位、同驱动,预热后丢弃前 N 次迭代,固定请求集与 seed,记录 --max-num-seqs。
  2. 读并行配置:从启动日志取出实际生效的 pipeline_parallel_size、tensor_parallel_size 与每步微批数,确认段数没有因整除约束被改写。
  3. 算气泡率上界:用估算式代入段数与微批数,得到理论气泡占比,与实测有效利用率对照。实测落差明显大于估算时,问题不在气泡,而在段间负载不均或段间传输。
  4. 看利用率形态:跨所有 rank 抓一段 trace,看是周期性全体塌陷(气泡)还是个别 rank 恒定低位(负载不均)。
  5. 分段计时:分别取 TTFT 与 TPOT / ITL。段数增加时 TTFT 抬升、TPOT 基本不变,说明代价落在管线深度,而不在单 token 生成。
  6. 调微批数复测:固定段数,把每步微批数调大,复测 goodput 是否回升。回升幅度接近估算式预测值时,气泡就是主因。
# 示意:固定工况的压测入口(参数名取自框架,取值按本机规格填)
vllm bench serve \
  --model <model-path> \
  --dataset-name random \
  --random-input-len 1024 --random-output-len 256 \
  --num-prompts 512 \
  --seed 1234 \
  --max-concurrency 64
# 示意:启动日志里与并行配置有关的那几行(字段名示意,非原文)
pipeline parallel size .........  <段数>
tensor parallel size ...........  <张量并行度>
max concurrency ................  <按池子反推的并发>

处置:参数与代价

动作 作用 代价 / 前提
调大微批数 摊薄填充与排空,气泡率按估算式下降 in-flight 微批增多,占用更多显存与调度开销
调小 pipeline-parallel-size 段数减少,气泡与管线深度都下降 每段层数增多,单段显存上升,可能装不下
均衡各段层数 消除最慢段对整条管线的钳制 需按实际层数与结构手工配平,段数受整除约束
段间换成高带宽链路 压小段间激活传递的等待 受拓扑约束;张量并行仍须留在机内
交错调度(交织多个虚拟段) 把填充与排空填进别的微批 调度更复杂,训练侧对应 1F1B 一类调度,推理侧收益随负载而变
用数据并行替代部分段 跨机不再逐段串行,延迟下降 需要多次复制整模型,显存与成本上升

顺序建议:先看气泡率估算与实测利用率的落差,落差与估算一致时优先调微批数;落差明显更大时先查各段负载是否均衡;两项都排除后再考虑段数与拓扑,段数增加会同时加深管线并放大气泡。

记录字段

# 示意:一条管线并行结论的记录(字段名示意)
model:
  layers:              # 总层数,用于判断能否按段数整除
  precision:
hardware:
  card_count:
  node_count:          # 跨机段数
  interconnect:        # 机内互联带宽 / 跨机链路类型
parallelism:
  pipeline_parallel_size:
  tensor_parallel_size:
  num_ubatches:        # 每步实际切分的微批数
workload:
  input_len:
  output_len:
  max_num_seqs:
perf:
  ttft:
  tpot:
  e2e_throughput:
utilization:
  occupancy:           # 资源占用率
  effective:           # 有效利用率
  bubble_ratio_est:    # 按估算式得到的气泡率上界
  stage_imbalance:     # 各段计算量是否均衡
status:                # 气泡主导 / 负载不均主导 / 段间传输主导

小结

管线并行把层切成段放到不同设备上,微批在管线首尾的填充与排空形成气泡,气泡率可按 (pipeline_parallel_size - 1) / (num_ubatches + pipeline_parallel_size - 1) 估算。同时使用两类并行时,张量并行每层都要 all-reduce,放在机内;管线并行只在段间传激活,放到跨机。遇到「PP 调大后延迟上升、利用率填不满」时,先算气泡率上界并与实测有效利用率对照,落差一致就调微批数,落差过大则先查各段负载是否均衡。