管线并行的气泡:微批切分、气泡率与设备利用率
在一组跨机部署的 **** 集群上(每机 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)。核对时以启动日志里实际生效的段数与每步处理的微批数为准。
组合顺序:张量并行在内、管线并行在外
两类并行同时用时,常规组合是张量并行放在机内、管线并行放到跨机。原因在通信频率:
- 张量并行每层都要 all-reduce,通信次数最多、对带宽最敏感,须放在机内最高带宽的链路上;
- 管线并行只在段与段之间传一次激活,通信次数少、单次数据量就是一段层的输入激活,频率远低于每层 all-reduce,可以放到带宽低一个量级的跨机链路。
rank 排列上表现为:张量并行是最内层,tensor_parallel_size 个 rank 落在同一机内的互连域里;管线并行是外层,跨机扩展。调度侧的拓扑约束也按这个口径落位——小规模张量并行作业落进一个机内互连域,大规模管线并行作业落进一个机架。把顺序反过来(张量并行跨机)会让每层 all-reduce 走低速链路,拐点大幅前移。
现象
管线气泡以几种可观测形态出现:
- 端到端吞吐不随段数线性上升,某个档位后持平甚至下降;
- 首 token 延迟随段数上升,因为一个请求要依次穿过所有段,管线越深、穿行时间越长;
- 设备利用率曲线周期性塌陷,填充与排空段各设备轮流失闲;
- 资源占用率高而有效利用率低:设备被任务占着,但计算单元长时间等待上游激活。
前两条指向气泡与管线深度,后两条指向「占用」与「有效利用」两套口径的落差。资源占用率回答机器是否被占住,有效利用率回答计算单元是否在高强度计算,两者不是一回事。
触发条件
- 段数大于 1 且微批数偏小:小批、单请求或低并发时,一个步里可切的微批数不足,填充与排空占比放大;
- 各段计算量不均衡:层数不能按段数整除、或某段含特殊结构时,最慢的段决定整条管线节奏,其余段等待;
- 跨机段间传输落在关键路径:段间激活传递走低速链路时,等待被放大;
- 与其它特性组合受限:管线并行与投机解码的组合在部分版本上不被支持;某些引擎的某些版本对管线并行的支持也是后来补上的,选型时须先核对版本窗口。
定位步骤
- 固定工况:同机、同卡位、同驱动,预热后丢弃前 N 次迭代,固定请求集与
seed,记录--max-num-seqs。 - 读并行配置:从启动日志取出实际生效的
pipeline_parallel_size、tensor_parallel_size与每步微批数,确认段数没有因整除约束被改写。 - 算气泡率上界:用估算式代入段数与微批数,得到理论气泡占比,与实测有效利用率对照。实测落差明显大于估算时,问题不在气泡,而在段间负载不均或段间传输。
- 看利用率形态:跨所有 rank 抓一段 trace,看是周期性全体塌陷(气泡)还是个别 rank 恒定低位(负载不均)。
- 分段计时:分别取 TTFT 与 TPOT / ITL。段数增加时 TTFT 抬升、TPOT 基本不变,说明代价落在管线深度,而不在单 token 生成。
- 调微批数复测:固定段数,把每步微批数调大,复测 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 调大后延迟上升、利用率填不满」时,先算气泡率上界并与实测有效利用率对照,落差一致就调微批数,落差过大则先查各段负载是否均衡。