做多卡推理选型的时候,我见过不少团队卡在一个很朴素的问题上:模型一跑就报 OOM,于是第一反应就是“这卡放不下,上并行”。然后打开框架文档,看到--tensor-parallel-size和--pipeline-parallel-size两个参数,随手填了个数字,凑够显存就算完事。这种做法在模型能跑起来之后,往往会留下一个看不见的坑:要么吞吐上不去,要么延迟飘得没法看,要么跨节点通信直接把集群打成广播电台。
TP 和 PP 的选型,表面上是个“显存放不放得下”的数数题,实际上是个“通信、计算、调度、业务延迟目标”的综合题。显存只是入场券,不是决策依据。下面的内容我不会去背书某个框架的文档,而是把这两种并行方式在多卡推理场景下到底在做什么、各自要付出什么代价、什么时候该用哪个,拆开讲一遍。
1. 先搞清楚 TP 和 PP 到底在“切”什么
很多教程喜欢用“张量并行是把模型切开,流水线并行是把模型分层”这种说法,听起来对,但不够准确。TP 和 PP 切的维度和代价模型完全不同,不搞清楚这一点,后面所有选型都是猜。
1.1 TP 是把“一层”拆成多份,让多张卡同时算同一层
张量并行,核心思想是把 Transformer 某一层里的权重矩阵按行或者按列切块,分布到多张 GPU 上。以线性层Y = XW为例,权重矩阵W可以横向切成几块,每张卡只保存其中一块,输入X也会做相应切分。前向计算时各卡先做自己那一块矩阵乘,得到部分结果,再通过集合通信把结果拼起来。
所以 TP 的特点是:一层计算由多卡协同完成。每一层都需要卡间同步。如果你开了 TP=8,模型的每一层都会把 QKV 投影、注意力输出投影、FFN 这些算子打散到 8 张卡上,每个算子之间都要穿插通信。显存上每张卡只放 1/8 的权重,但计算过程中通信量非常大,而且随着 TP 度数上升,通信占比不是线性增长,是接近指数级地吃掉你的算力。
这里有个关键概念:TP 对通信带宽极其敏感。单机 8 卡通过 NVLink 全互联,做 allreduce 很快;但如果把 TP 拉到跨节点,走万兆以太网或者 RoCE,每层都跨机同步一次,延迟会飙升到不可接受。我实际测试过,跨机 TP 的每 token 生成延迟比单机 TP 高出数倍,而且 GPU 利用率反而下降,因为大部分时间都卡在等通信。
1.2 PP 是把“前后层”分给不同卡,卡间传递的是中间激活
流水线并行切的是层的纵向顺序。假设模型有 48 层,PP=2,那就前 24 层放 GPU0,后 24 层放 GPU1。输入数据先经过 GPU0 算出第 24 层的输出,再把这份中间结果传给 GPU1,继续跑后 24 层。
这个模式下,每张卡保存的是它负责的那一段层的完整权重。注意这里和 TP 有本质区别:PP 没有把任何一层的权重切开,而是把完整的一块块层分散到不同卡上。所以它对显存的节省是“分段式”的,而不是“均分式”的。
PP 的通信量比 TP 小得多。数据只在流水线的段与段之间传一次,不像 TP 那样每层每算子都要同步。因此 PP 对通信带宽的要求没那么高,跨节点跑 PP 比跨节点跑 TP 合理得多。但 PP 有个经典问题叫“流水线气泡”:如果 GPU0 在算前几层,GPU1 此时手上没活,只能空等。数据一批批地灌进流水线,在第一个 batch 还没流到 GPU1 之前,GPU1 的算力是浪费的。流水线越长,气泡浪费越大。
1.3 模型太大时,TP 和 PP 通常不是二选一
实际部署超大模型时,主流方案是“TP 做卡内切分、PP 做跨机切分”。这个思路的逻辑是:在单机内用 NVLink 扛住 TP 的高频通信,在机间用相对低频的 PP 传递中间激活。
比如你要部署一个 70B 级别的稠密模型,BF16 权重约 140GB,单台 8×80GB 的机器勉强放下权重,但 KV cache 和调度余量几乎没有。更合理的结构是 TP=4 把每层权重切到 4 卡上,再叠加 PP=2 把模型层分成两段放到两组卡上,最终用 8 张卡组成一个实例。
这个组合在训练里很常见,在推理里也开始被 vLLM、SGLang、TensorRT-LLM 等框架逐步支持。但你要清楚,TP 和 PP 的代价不是简单相加,而是叠加的:TP 引入每层通信,PP 引入跨段通信和气泡,组合之后调度逻辑也会更复杂。模型没大到单机装不下时,不要为了“炫技”去开 PP。
2. 为什么“显存放得下”从来不是充分条件
显存是否够用,是人最容易做的评估。拿权重文件大小除以 GPU 显存,再乘个系数,觉得能放下就上并行。这种算法忽略了三个推理场景才能真正决定成败的东西:动态显存、通信开销和延迟约束。
2.1 KV cache 让显存使用量变成一个动态区间
大模型推理不像跑一次训练前向那么简单。每个请求进来,都要在显存里分配一块 KV cache,用来缓存历史 token 的 Key 和 Value。这部分显存是动态增长的,长度越长,并发请求越多,占的显存就越大。
一个 7B 模型,BF16 权重大约 14GB,单卡看起来毫无压力。但如果服务开 64 并发,每路上下文 32K,KV cache 可能吃掉 20GB 以上,再加上激活值和 CUDA context,单卡照样会 OOM。所以“模型权重放得下”和“这个推理服务放得下”完全不是一回事。
选型时一定要把 KV cache 预留掉。实操上我会先估算峰值并发和平均上下文长度,给 KV cache 留足余量,再看剩余空间能放多大的权重块。很多团队开 PP 的动机居然是“权重切开后每卡剩余显存更大”,如果把这个账算清楚,就会发现单机内其实用 TP 更省事,剩余显存一样可以留给 KV cache,而且通信代价更可控。
2.2 TP 的通信开销会吃掉你加卡带来的全部收益
加卡的目的是什么?绝大多数推理场景加卡,不是为了单纯把模型塞进去,而是为了提升吞吐或降低延迟。可在小模型场景下,盲目加 TP 反而会让性能倒退。
我之前做过一个对比测试:8B 模型 FP16 约 16GB,单张 A100 80GB 完全可以放下。为了提升并发吞吐,把 TP 开到 2 跑,结果单请求首 token 延迟反而比单卡慢了约 15%。原因很简单:每层多了一次通信同步,计算量并没有因为切成两份而变少,通信却成了新瓶颈。吞吐只有在并发打到一定程度后才追回来,低并发场景完全是负优化。
这个现象说明了关键问题:TP 的收益来自把大矩阵乘法拆到多卡并行计算,从而降低单卡计算时间,但它能赢的前提是“单卡算力真的不够”,而不是“显存不够”。一个 7B 模型在 A100 上的瓶颈很多时候是显存带宽和算子效率,不是显存容量,不是算力不足。你给它开 TP,它并不会变快,只会多出一堆卡间通信。
2.3 PP 的气泡浪费是隐形成本,越深的流水线越明显
PP 的显存账倒是很简单,但它的代价体现在 GPU 空转上。一个深度为 p 的流水线,理想情况下气泡占比约等于 (p-1)/(p+m-1),m 是 micro-batch 数量。m 越大气泡越小,但这意味着你要有足够大的 batch 去填充流水线。
在线推理服务的 batch 是动态的,不是训练时那种固定的 micro-batch 流。请求稀疏时,PP 后段的 GPU 会大面积闲置。很多团队上了 PP 之后发现 GPU 利用率只有 30%,排除了显存问题、算子问题,最后才发现是流水线根本没被填满。
所以选择 PP 前要问自己:你的业务请求量稳定吗?能达到持续灌满流水线的程度吗?如果只是偶尔有几个测试请求,PP 就是纯纯的浪费。这也是为什么很多开源推理框架对 PP 的支持默认不开启——它更适合高吞吐的离线批量推理,而不是低延迟的在线交互。
2.4 不要忽略显存带宽和算力的匹配问题
还有一个容易被忽略的点:显存够,但带宽不够。LLM 推理的 decode 阶段是典型的访存密集型任务,每生成一个 token 要把全部权重读一遍,GPU 的显存带宽决定了生成速度的理论上限。
A100 80G 的显存带宽约 2TB/s,读一遍 14GB 权重的理论下限是 7 毫秒左右。多卡并行时,每张卡只读自己那份权重,所以 TP=2 理论上能把单 token 的权重读取时间降到约 3.5 毫秒,但实际因为通信开销,可能只能到 5 毫秒。这背后的权衡很简单:你切开权重省了访存时间,但引入了通信时间;如果通信时间大于省的访存时间,就是亏本买卖。
所以决策时,不要只看“放不放得下”,还要看“切了之后每卡权重变小省下的读取时间,能不能覆盖通信增加的时间”。这需要实测,不能拍脑袋。
3. 多卡推理配置的完整估算和选型流程
结合项目经验,我把可落地的选型流程拆成六步,每一步都有明确的判断依据。
3.1 第一步:把真实显存账算清楚
不要拿model.safetensors的文件大小来算显存。正确的公式如下:
- 权重占用 = 参数量 × 每个参数的字节数。BF16 是 2 字节,FP8 是 1 字节,INT4 量化后约 0.5 字节。
- KV cache 占用 = 2(Key 和 Value)× 层数 × 头维度 × 上下文长度 × 并发数 × 每元素字节数。
- 激活和临时 buffer 按 1-2GB 估算,CUDA context 按 0.5-1GB 估算。
举一个实际例子,假设你要部署一个 70B 的 BF16 模型,参数量 70B:
- 权重:70 × 10^9 × 2 = 140GB
- 单张 A100 80G,如果只装权重也要 2 张卡,但没有任何余量给 KV cache
- 要支撑 32 路并发、上下文 4096 的 KV cache,粗略估算需要多出 40-60GB
纯显存角度,至少需要 4 张 80G 卡。所以 TP=4 是起步,不是“够不够”的问题,而是你本来就不可能用 2 张卡支撑有意义的并发。
3.2 第二步:单卡优先,尽量把并行留到最后
我的习惯是:显存能塞下、KV cache 也有余量,就先跑单卡。量化到 FP8 或者 INT4 之后能用单卡跑的,就不要上 TP。很多 70B 模型用 INT4 量化后只有 35-40GB,单张 80G 能跑,双卡都多余。
单卡的优势不光是省事,还在于没有通信开销、没有并行调度开销、调试简单。多卡并行会引入很多奇怪的问题,多机通信超时、设备间版本不一致、负载不均……这些都不是模型本身的问题,但会让你排查到怀疑人生。
3.3 第三步:看你的硬件拓扑是“一机”还是“多机”
这是决定 TP 和 PP 的最核心因素。单机内如果 GPU 之间有 NVLink 或者 NVSwitch 全互联,优先 TP,因为 TP 需要高频通信,只有 NVLink 这种高带宽低延迟的互联才扛得住。如果只有普通 PCIe 互联,TP 要谨慎,PCIe 的带宽相比 NVLink 差一个数量级,开 TP=8 之后大概率通信成为瓶颈。
多机场景基本不要考虑跨机 TP。正确做法是每台机器内部先用 TP,机器之间再用 PP,也就是常见的“TP within node, PP across nodes”。PP 跨机传递的只是中间激活,频率低、数据量相对小,对网络要求比 TP 低得多。
这方面我踩过的坑是:早期图省事,直接用分布式框架默认配置,把 TP=16 挂到两台 8 卡机器上。结果跑起来之后 NCCL 初始化正常,但真正的瓶颈是机间网络,整个集群只能达到 30% 左右的利用率。后来改成每机 TP=8、机间 PP=2,同样显存配置,吞吐直接翻倍还多。
3.4 第四步:根据服务类型定体验目标
在线聊天类服务关注首 token 延迟和生成速度,TP 往往更友好,因为它不引入流水线气泡,每个请求都能被所有卡并行算。离线批量处理关注整体吞吐,PP 的优势能发挥出来,因为你可以攒足 batch 灌满流水线,让气泡率降到最低。
有一个比较形象的类比:TP 就像一条多车道高速公路,每辆车都能同时用所有车道;PP 像工厂流水线,得先把足够多的半成品排进去,最后一个工位才不会闲着。如果你的客户是“一辆一辆零散来的车”,修高速比开工厂合适;如果业务是“一批一批来的订单”,开工厂更省地皮。
3.5 第五步:跑一次有代表性的压测,而不是只测能不能生成
配置完成后不要只跑一个“你好”测试。要模拟真实流量:多路并发、长上下文、混合长度请求。压测时主要看这几个指标:
| 指标 | 含义 | 异常信号 |
|---|---|---|
| TTFT | 首 token 延迟 | TP 跨节点时明显恶化 |
| TPOT | 每 token 生成时间 | 显著高于单卡理论值说明通信瓶颈 |
| 吞吐 | tokens/s | PP 在小并发下吞吐上不去 |
| GPU 利用率 | 平均利用率 | 持续低于 60% 优先查流水线或通信等待 |
| 显存峰值 | 平均显存占用 | 接近 100% 说明 KV cache 余量不足 |
这个压测结果会直接推翻很多纸面上的“显存推断”。我遇到过不止一次,模型权重在 4 卡上完全放得下,但实际压测到 32 并发就 OOM,因为 KV cache 预留没做好。这时候需要调整的是并发上限、上下文长度上限或者量化精度,而不是再加 TP 度数。
3.6 第六步:根据压测结果显示的风险来微调
如果 TTFT 和 TPOT 都达标,但显存峰值逼近上限,首先要做的不是加卡,而是检查是不是有请求把上下文拉得太长,或者在框架里设置了过大的 max-model-len。把max-model-len从 32K 降到 16K,显存占用可能立刻降一半。
如果 GPU 利用率低,且日志里频繁出现nccl超时,大概率是 TP 跨了慢速互联。把 TP 缩小、PP 扩大试试。如果压测吞吐很好,但单请求延迟高,可能是框架的 continuous batching 策略把请求排得太满,和并行策略关系不大,需要从调度参数层面调。
4. Prefill 和 Decode 阶段决定了 TP 和 PP 的实际表现
前面说的都是一般情况。真正做过推理优化的人会知道,一个请求在 LLM 服务里会被拆成两个阶段:prefill 阶段处理整段输入,计算量大但只跑一次;decode 阶段逐 token 生成,计算量小但要循环很多次。TP 和 PP 在这两个阶段里的表现是完全不同的。
4.1 Prefill 阶段,TP 的收益非常直接
Prefill 是矩阵乘法密集阶段,输入的 tokens 数量大,非常适合并行计算。TP 把注意力头和多头线性层拆到多卡上,prefill 时间理论上可以接近线性缩短。比如单卡 prefill 要 10 秒,4 卡 TP 理想情况只要 2.5 秒,再加上通信损耗,实际能做到 3-4 秒就很理想了。
所以如果你的业务有大量长文档输入,用户会明显感知到“上传完资料等多久才开始回复”,TP 是改善体验的利器。PP 在这个阶段没有这个效果,它只是把你的输入按顺序流过各层,总 prefill 时间并不会因为卡变多而显著降低,反而多一跳跨机传输。
4.2 Decode 阶段,TP 的通信代价被无限放大
Decode 阶段一次只算一个 token,矩阵乘的尺寸其实很小。这时候 TP 的切分收益很低,但每生成一个 token 依然要做一次全量的梯度同步式通信——如果用的是权重同步的 allreduce 范式。于是在 decode 很长的对话里,TP 的卡间通信次数 = 生成 token 数 × 层数 × 算子数,这个开销会一直累积。
这也是为什么有些模型在纯离线批量 prompt 测试下 TP 表现很好,一到交互式长对话场景就原形毕露。解决办法有两个方向,一是减少 TP 度数,让每张卡多算一点、少通信一点;二是把 decode 阶段切分成更粗粒度的并行,让一次通信能服务于更多计算。
4.3 PP 天然适合 decode,但前提是 batch 足够大
PP 的 decode 阶段,数据像流水线一样经过各个 GPU 段,不会每算子都通信。只要 batch 足够,每个 GPU 段都在同时处理不同请求的不同层,理论上能达到很高的吞吐。这跟 CPU 流水线设计的思路如出一辙:单条指令跑流水线没有优势,指令多了吞吐才上来。
但在连续推理框架里,请求是动态进出和动态拼接的,流水线的排空和排空都会造成气泡。假设 batch 只有 4 个请求,前段 GPU 在算,后段 GPU 就得等前 4 个完成计算才能接到活。所以 PP 在真实在线流量下要达到高利用率,必须配合足够大的 batch 以及能打散请求的调度策略。
4.4 阶段分离架构是当前推理框架的折中方案
现在一些框架在思考如何把 prefill 和 decode 分别部署到不同实例上,prefill 实例用大 TP 提高上行计算吞吐,decode 实例用小 TP 降低通信开销。这已经超出了“单实例 TP/PP 选型”的范畴,变成一种系统级并行设计。
如果你的项目规模够大,可以在梳理完 TP 和 PP 的底层逻辑后,再考虑这种分离式部署。先从单实例的 TP/PP 入手把业务跑通,把显存、吞吐、延迟的基线数据拿到手,再谈分离,而不是项目刚启动就上一套复杂的分布式架构。
5. 常见问题和排查心得
多卡推理的坑比较隐蔽,而且出错时往往不会直接告诉你“TP 度数不对”或者“PP 气泡太大”,而是表现为“显存明明够但不稳定”“速度忽快忽慢”“通信初始化失败”。
5.1 明明显存够用,但还是 OOM 了
这种情况十有八九是 KV cache 或激活值出了问题。权重是静态显存,KV cache 是动态显存。长上下文的请求一多,KV cache 会暴涨。排查方法是在框架日志里临时把最大并发和上下文限制调低,看是否仍然 OOM。如果调低后正常,说明不是并行策略的问题,是容量规划没算动态部分。
另外注意,在多个推理实例共用一张卡的时候,要留出足够显存给 CUDA context 和其他进程。有时候 nvidia-smi 显示显存还剩 3GB,但下一个推理进程就是起不来,因为 CUDA context 需要连续显存块,而剩余空间可能已经被碎片化。
5.2 TP 度数越高,单 token 延迟反而变大
如果你加 TP 后吞吐没有明显提升,甚至延迟变高,大概率是通信开销盖过了计算并行收益。这时候要先看 GPU 互联拓扑:用nvidia-smi topo -m查看卡间通信是否走 NVLink,如果显示走 PCIe,那 TP=4 以上基本是负优化。另外要看框架配置的 NCCL 环境变量,确保使用 NVLink 而不是走 IB 或者 TCP。
还有一种可能:模型本身不够大,不需要 TP。7B、8B 级别的模型在 A100/H100 上更强的是单卡推理,别为了填满 8 卡而强行 TP=8。多卡虽然显存变大,但单 token 延迟不会紧跟变低,因为单卡算力没有成倍增长。
5.3 PP 开启后,总吞吐不如不开
PP 是更宏大的并行策略,但如果 batch 不够大,气泡占比会很高。我在批量跑一个 70B 模型时,PP=2 的吞吐只有 TP=4 加单层切分的四成左右,原因就是请求很稀疏,流水线后段多数时间在空转。
排查手段是持续观察各 GPU 的利用率曲线。如果利用率呈现明显的“波浪式”起伏,高高低低周期性出现,基本就是流水线气泡。要解决就两条路:一是用更大的 batch 填满流水线;二是把 PP 度数降低,或者干脆去掉 PP 改用大 TP。如果调度允许,还可以引入多租户并发,让不同的推理任务共享同一条流水线,把空档填上。
5.4 跨节点部署时频繁报 NCCL 超时
跨节点做并行训练时这类问题很常见,但推理场景也躲不开。原因通常是机间网络延迟没有达到要求,或者在容器环境里 shm 大小不够。NCCL 对共享内存的依赖比较大,容器默认的/dev/shm只有 64MB,很容易不够用,要显式调大。
治理思路是按优先级排:优先同机 NVLink 互联的 TP,其次同机 PCIe 互联的小 TP,再考虑跨机 PP。如果一定要跨机做并行,网络最好走 RoCE 或者 InfiniBand,普通千兆以太网只适合跑数据并行,不适合多层并行协同。
5.5 显存碎片化导致多卡显存不均衡
多卡推理部署久了,不同 GPU 上的显存使用量可能越来越不均衡。有的卡被老的 KV cache 占着不放,有的卡权重多一块。这时候重启进程能解决大部分问题,但更根本的办法是给每个实例设置独立的显存上限,并启用框架的显存复用机制。
打个比方,这就像仓库管理。显存只是“仓库总面积”,但如果货物堆放不整齐、过道被占满,实际可用的存储空间远小于理论值。并行策略和显存规划要配套,静态权重切得再均匀,动态缓存调度不合理一样会卡脖子。
6. 我给多卡推理选型的最终建议
把项目里的实际经验压缩成几句白话,不一定适合所有场景,但值得参考。
模型不到 30B 的规模,先做量化,能用单卡跑就不要开并行。30B 到 100B 之间,单机多卡优先考虑 TP,TP 度数一般控制在 4 或 8 以内,前提是卡间要有 NVLink。超过 100B 且超过单机承载能力时,才认真考虑机内 TP 加机间 PP 的组合,并对流水线气泡做专门的 batch 调度优化。
选型时把“显存能不能塞下”当成第一道过滤网,但真正拍板要依靠压测数据。没有压测数据之前,任何纸上推导都只能算是假设。我见过的最多翻车案例,不是技术方案不够先进,而是把“显存正好够放”当成了“系统一定稳定”,结果一上并发流量就崩。
如果你现在正要给一个模型选并行策略,我的建议是:把模型按我说的方法估算一遍,然后先跑单卡,再测 TP=2,再测 TP=4,有条件的话测一下 TP=2+PP=2 的组合,形成一张性能对照表,再看业务是重首 token 延迟还是重吞吐。这个测试流程看上去慢,其实半天就能完成,远比直接上大 TP、大 PP 之后反复调优要省时间。
最后再分享一个小技巧:框架日志里如果出现让你摸不着头脑的通信错误,先把并行度降到一个非常保守的配置,比如 TP=1、PP=1,确认模型能正常跑通,再逐步往上加并行度。这样能快速确定问题是出在模型本身,还是出在并行配置上。这个二分排查法帮我省掉过无数个排查之夜,希望你也能用它避坑。