4、Pipeline Parallelism(PP)基础

Pipeline Parallelism,简称PP,中文叫流水线并行。说白了,就是把一个大模型切成好几段,每段放在不同的GPU上。我刚开始接触这个概念时,觉得这不就是“切香肠”嘛——把模型切成几块,每块GPU负责一块。

但实际用起来,里面的门道可不少。今天我就把PP的底裤扒干净,从概念到配置,一条龙讲清楚。

4.1 PP的核心思想:把模型切成“香肠”

想象一下,你有一个70B的大模型,单张A100放不下。怎么办?

PP的做法是:按层切。比如把前10层放在GPU0,中间10层放GPU1,最后10层放GPU2。数据从GPU0开始,算完传给GPU1,再传给GPU2,最后输出。

嗯,这里要注意:PP切的是模型层,不是数据。这和后面要讲的数据并行(DP)完全不同。

关键区别:

  • 数据并行(DP):每张GPU都有完整模型,只切数据
  • 流水线并行(PP):每张GPU只有部分模型,数据完整流过

我在项目中遇到过一位同事,把PP和DP搞混了,结果配了半天发现显存还是爆了。嗯,这两个概念一定要分清楚。

4.2 微批次划分:让GPU别闲着

PP有个大问题:GPU会“空转”。

为什么会这样?你想想看,GPU0算完第一批数据,传给GPU1。这时候GPU0就没事干了,得等GPU1算完才能接下一批。这效率多低啊!

解决方案就是——微批次(Micro-batch)。

把一个大批次(比如32条数据)切成4个微批次(每批8条)。GPU0算完微批次1,立刻传给GPU1,然后马上开始算微批次2。这样GPU0就不用干等了。

我的经验:微批次数量一般设为GPU数量的2-4倍。比如4张GPU,微批次设8-16个。太小了气泡多,太大了通信开销大。

我曾经踩过一个坑:微批次设得太大,结果显存爆了。因为每个微批次都要保存中间激活值,微批次越多,显存占用越大。嗯,这里要平衡好。

4.3 PP vs TP:到底选哪个?

很多新手会问:PP和TP(张量并行)到底有啥区别?我该用哪个?

我直接给你一张对比表,看完就明白了:

维度 PP(流水线并行) TP(张量并行)
切分方式 按层切 按张量维度切
通信频率 低(每层一次) 高(每步都要通信)
通信量 大(整个激活值) 小(部分张量)
适用场景 跨节点、慢网络 单节点、NVLink
显存节省 显著(每卡只存部分层) 中等(每卡存部分参数)
实现复杂度

我个人习惯:能TP就TP,TP不够再加PP。因为TP的通信效率更高,GPU利用率也更好。但如果你跨节点部署,网络带宽有限,那PP就是首选。

避坑指南:我曾经在4节点、每节点8卡的集群上,先用了TP(8卡内),再用PP(跨节点)。结果TP的通信把NVLink带宽吃满了,PP的跨节点通信反而成了瓶颈。后来我调整了策略:每个节点内用TP,节点间用PP,效果好了很多。

4.4 PP配置参数详解

在vLLM里,PP的配置主要通过这几个参数控制:

# vLLM启动示例
python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Llama-2-70b-hf \
    --tensor-parallel-size 4 \
    --pipeline-parallel-size 2 \
    --max-num-seqs 32 \
    --max-model-len 4096

关键参数说明:

  • pipeline-parallel-size:PP的度数,即把模型切成几段。比如设为2,就是切成2段,放在2张GPU上。
  • tensor-parallel-size:TP的度数。注意,TP和PP是相乘关系。比如TP=4,PP=2,总共需要8张GPU。
  • max-num-seqs:最大并发序列数。这个值会影响微批次的划分,设大了显存占用高,设小了GPU利用率低。
  • max-model-len:最大序列长度。PP下这个值会影响每个微批次的大小。

配置建议:

  • 单节点(8卡以内):优先用TP,PP设为1
  • 多节点(16卡以上):每个节点内TP,节点间PP
  • PP度数建议不超过4,太大了气泡太多
  • 微批次数量 = pipeline-parallel-size × 2 ~ 4

我记得有一次帮客户调优,他们用了PP=8,结果吞吐量反而下降了。一查发现,微批次数量没调,还是默认的4。气泡太大了,GPU大部分时间都在空等。后来把微批次调到16,吞吐量直接翻倍。

4.5 PP的显存计算

很多人会问:PP到底能省多少显存?

我直接给公式:

每卡显存 ≈ 模型总显存 / PP度数 + 激活值显存

举个例子:70B模型,FP16精度,大约需要140GB显存。如果PP=2,每卡只需要存70GB的模型参数。再加上激活值,大概每卡80-90GB。两张A100(80GB)勉强能跑。

但注意:激活值显存不会因为PP而减少太多。因为每张GPU还是要保存自己那部分的中间结果。

我的小技巧:如果显存刚好卡在边界上,可以试试把微批次数量调小一点。虽然吞吐量会降一些,但至少能跑起来。我曾经用这个方法,在4张A100上跑通了70B模型,虽然慢了点,但总比跑不了强。

4.6 PP的通信模式

PP的通信其实很简单:点对点通信(P2P)。

每张GPU只需要和前后邻居通信。GPU0传给GPU1,GPU1传给GPU2,以此类推。不需要像TP那样搞all-reduce。

这也是PP适合跨节点的原因——通信模式简单,对网络带宽要求不高。

但要注意:通信量很大。每次传输的是整个激活值,而不是部分张量。比如序列长度4096,隐藏维度8192,FP16精度,一次传输就是4096×8192×2=67MB。如果微批次多,这个量会成倍增加。

我曾经踩过的坑:在InfiniBand网络上跑PP,没开RDMA,结果通信成了瓶颈。后来开了RDMA,延迟从200微秒降到了10微秒,吞吐量提升了30%。嗯,网络配置一定要优化到位。

4.7 总结:PP的适用场景

说了这么多,到底什么时候该用PP?

  • 模型太大:单张GPU放不下,TP也救不了
  • 跨节点部署:节点间网络带宽有限,PP的通信模式更友好
  • 显存紧张:PP能显著降低每卡显存占用
  • 不想搞太复杂:PP的实现比TP简单得多

但也要记住PP的缺点:GPU利用率低(气泡问题)、延迟高(串行执行)。如果你的场景对延迟敏感,或者GPU资源充足,那还是优先考虑TP。

好了,PP的基础就讲到这里。下一节我们聊聊更高级的话题——PP的调度策略和优化技巧。


公众号:蓝海资料掘金营,微信deep3321