背景问题:模型放得下,不等于服务跑得稳
当模型规模从 7B、13B 增长到 70B、100B 以上时,单张 GPU 往往不再足够。团队通常会先想到两个方案:换更大显存的 GPU,或者把模型切到多张 GPU 上。前者简单但成本高,后者灵活但会把问题从”显存够不够”转成”跨卡通信稳不稳”。
在生产推理里,Tensor Parallel 的主要价值不是让系统看起来更分布式,而是把同一个模型副本的矩阵计算拆到多张 GPU 上,使单个大模型能够以一个逻辑副本对外服务。它解决的是”单副本放不下或单卡计算太慢”的问题,而不是普通水平扩容的问题。
这也是很多 LLM Serving 项目第一次上多卡时容易翻车的地方:
- 模型能启动,接口能返回,但 P95/P99 延迟非常抖
- 压测单请求正常,并发一上来就 NCCL timeout
- 同一组 GPU 换了槽位或节点,吞吐差异明显
- 跨节点部署后 TTFT 变长,TPOT 也不稳定
本文重点讨论:如何选择 tensor_parallel_size 和 pipeline_parallel_size,如何理解 NCCL 通信成本,如何按 GPU / NIC 拓扑做部署约束,以及上线前应该压测什么。
核心原理:Tensor Parallel 本质上是在每层制造通信
Tensor Parallel 切的是层内矩阵
在 Transformer 推理中,注意力层和 MLP 层都包含大量矩阵计算。Tensor Parallel 会把一层中的权重矩阵按列或按行切分到多张 GPU 上,让这些 GPU 同时计算各自的分片,然后再通过集合通信把结果合并。
这与 Data Parallel 完全不同:
| 并行方式 | 切分维度 | 解决的问题 | 通信特点 |
|---|---|---|---|
| Data Parallel | 多副本处理不同请求 | 吞吐不足 | 梯度同步(训练)/ 无同步(推理) |
| Tensor Parallel | 单副本层内矩阵切分 | 单卡放不下/算力不足 | 每层都可能引入跨卡同步 |
| Pipeline Parallel | 单副本层间切分 | 单节点放不下 | 层间传递,存在 pipeline bubble |
Hugging Face Transformers 的 Tensor Parallel 文档将其描述为”将模型层切成多个片段,让多个加速器同时工作”,并明确提示需要快速的节点内通信,因为 GPU 会在每一层交换部分结果。这个描述非常适合生产团队理解它的成本边界:Tensor Parallel 的收益来自并行计算,成本来自每层通信。
NCCL 是跨 GPU 通信的关键路径
在 NVIDIA GPU 集群里,NCCL 是最常见的集合通信库。它提供 AllReduce、Broadcast、Reduce、AllGather、ReduceScatter、AlltoAll 等通信原语,也支持点对点 send/receive。Tensor Parallel 推理中,常见瓶颈并不是某个普通 HTTP 接口,而是这些集合通信在每层、每个 batch、每个 decode step 上的累计开销。
典型链路可以抽象为:
request -> tokenizer -> GPU shard 0/1/2/3 compute
-> NCCL collective communication
-> next layer compute
-> NCCL collective communication
-> logits -> sampling -> token response
如果 GPU 之间有 NVLink 或 NVSwitch,节点内 Tensor Parallel 往往更容易跑稳。如果只是 PCIe,尤其是跨 CPU socket 或跨 NUMA 域,通信延迟和带宽都会更敏感。跨节点 Tensor Parallel 对网络要求更高,通常需要 InfiniBand、RoCE、GPUDirect RDMA、正确的 NIC 选择和容器共享内存配置。
Pipeline Parallel 切的是层间阶段
Pipeline Parallel 与 Tensor Parallel 的切分维度不同。它把模型按层拆成多个阶段,例如前 20 层在节点 A,后 20 层在节点 B。这样可以减少某些层内 AllReduce,但会引入 pipeline bubble:某些阶段等待前一阶段输出,GPU 不能一直满负荷工作。
vLLM 的并行扩展文档给出的实用建议是:
- 如果模型适合单 GPU,不需要分布式推理
- 如果模型太大但能放进单节点多 GPU,使用 Tensor Parallel
- 如果模型太大单节点放不下,把 Tensor Parallel 与 Pipeline Parallel 结合起来
- 通常将
tensor_parallel_size设置为每节点 GPU 数,将pipeline_parallel_size设置为节点数
这个经验适合绝大多数首版生产部署。
工程落地:先做并行度选择,再做拓扑约束
第一步:按显存和模型副本选择并行策略
可以按下面的顺序做初始判断:
- 单卡能放下模型、KV cache 和目标并发:优先单卡副本 + Data Parallel 水平扩容
- 单卡放不下,但单节点多卡能放下:优先 Tensor Parallel
- 单节点多卡仍放不下:Tensor Parallel + Pipeline Parallel
- GPU 数不能均匀切分模型,或节点内没有高速互联:评估 Pipeline Parallel 是否比 Tensor Parallel 更稳定
vLLM 文档中还有一个很实用的细节:启动后查看日志里的 GPU KV cache size 和 Maximum concurrency。前者表示 GPU KV cache 能同时存储多少 token,后者估算在给定 max_model_len 下能支持多少并发。如果这个估算低于业务要求,继续调并行度或加 GPU 才有意义;否则单纯调入口队列和网关限流不会解决底层容量问题。
第二步:把并行参数显式固化
不要让生产服务”自动猜”并行度。并行度应该成为发布配置的一部分,并纳入回滚记录。
一个 vLLM 单节点 4 卡 Tensor Parallel 示例:
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--tensor-parallel-size 4 \
--max-model-len 32768 \
--gpu-memory-utilization 0.90
一个 2 节点、每节点 8 卡的组合示例:
vllm serve /models/llama-70b \
--tensor-parallel-size 8 \
--pipeline-parallel-size 2 \
--distributed-executor-backend ray
这些参数不是一次性命令行细节,而是发布版本的一部分。建议把它们写入模型服务配置表,并与模型版本、量化版本、max_model_len、batch 参数、GPU SKU、节点池标签一起记录。
第三步:给 GPU 和 NIC 做亲和性约束
多 GPU 推理的稳定性高度依赖拓扑。生产部署时至少要记录以下信息:
nvidia-smi topo -m
nvidia-smi -L
ibstat || true
NCCL_DEBUG=INFO vllm serve ...
如果是 Kubernetes,应避免调度器把多卡任务随意放到不合适的节点或跨 NUMA 拓扑较差的位置。vLLM 文档中提到,为 GPUDirect RDMA 配置容器时,需要关注 IPC_LOCK、/dev/shm、共享内存挂载等条件;并建议通过 NCCL_DEBUG=TRACE 查看日志,确认是否出现 NET/IB/GDRDMA,而不是退化为 NET/Socket。
一个 Kubernetes 侧的简化约束示例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: llm-tp-server
spec:
template:
spec:
nodeSelector:
gpu.interconnect: nvlink
rdma.enabled: "true"
containers:
- name: vllm
image: vllm/vllm-openai:latest
securityContext:
capabilities:
add: ["IPC_LOCK"]
resources:
limits:
nvidia.com/gpu: 8
volumeMounts:
- mountPath: /dev/shm
name: dshm
volumes:
- name: dshm
emptyDir:
medium: Memory
关键是把 GPU 互联、RDMA 能力、节点池隔离、容器共享内存这些条件配置化,而不是靠人工记忆。
压测方法:把 prefill、decode 和通信分开看
不要只看总 tokens/s
总吞吐很容易掩盖问题。一个多卡部署可能在平均 tokens/s 上很好看,但 P99 TPOT 非常差,原因是某些请求触发长上下文 prefill、某些 batch 下通信拥塞、某些 NIC 路径退化为 TCP socket。
建议至少拆出这些指标:
| 指标 | 关注点 |
|---|---|
| TTFT(首 token 时间) | 排队、prefill、模型加载状态 |
| TPOT / ITL(每输出 token 间隔) | decode 阶段的跨卡同步成本 |
| P95 / P99 latency | 尾延迟,不要只看平均值 |
| GPU compute utilization | 算力是否真的被利用 |
| GPU memory / KV cache 使用率 | 是否快接近 eviction 或 OOM |
| NCCL collective 耗时 | 通信是否成为主瓶颈 |
| 跨节点网络吞吐、丢包、重传、RoCE ECN/PFC 状态 | 跨节点 TP 必看 |
压测矩阵要覆盖并行度组合
建议建立一个小型矩阵,而不是只测一个配置:
model: 70B instruct
input tokens: 512 / 4k / 16k / 32k
output tokens: 128 / 1k / 4k
parallelism:
- TP=1, DP=N
- TP=2, DP=N/2
- TP=4, DP=N/4
- TP=8
- TP=8, PP=2
metrics:
- TTFT p50/p95/p99
- TPOT p50/p95/p99
- tokens/s
- request/s
- GPU memory
- NCCL time
- error / timeout
如果 TP 从 4 增加到 8 后吞吐没有明显提升,甚至尾延迟变差,通常说明通信开销已经抵消了计算收益。此时不应继续盲目增加 TP,而应评估更合适的模型量化、减少 max_model_len、改用更多 Data Parallel 副本,或者把跨节点 TP 改成单节点 TP + 多副本负载均衡。
让 NCCL 日志进入灰度检查
NCCL 文档强调 collective operation 需要每个 rank 以相同 count 和 datatype 调用,否则可能出现 hang、crash 或数据损坏。线上表现往往不是”立刻报错”,而是卡住、超时、重启、某些 rank 长时间无响应。
灰度时建议启用临时详细日志:
export NCCL_DEBUG=INFO
export NCCL_DEBUG_SUBSYS=INIT,GRAPH,NET,COLL
观察重点包括:
- 是否使用期望的网络接口
- 是否启用 InfiniBand / RDMA / GDRDMA
- 是否出现 socket fallback
- 是否有 rank 初始化慢或 communicator 超时
- 是否跨 NUMA、跨 socket 或跨低速链路
日志量较大,不建议长期全量开启 TRACE,但灰度、压测和故障复盘阶段必须保留样本。
常见误区
误区一:GPU 越多越快
Tensor Parallel 不是免费的。每增加一张 GPU,计算切得更细,但通信同步也更多。对于小 batch、短输出或没有高速互联的节点,更多 GPU 可能降低单请求延迟稳定性。
误区二:Tensor Parallel 可以替代 Data Parallel
两者解决的问题不同。Tensor Parallel 解决单副本模型放不下或单卡计算不足;Data Parallel 解决多请求吞吐。实际生产中经常是 TP 组成一个大模型副本,再用多个副本做 DP。
误区三:只要模型启动成功就可以上线
多卡模型启动成功只说明权重加载和基本通信可用。它不能证明长上下文、并发 batch、错误恢复、节点重启、网络退化、NCCL 超时都可控。上线前必须压测目标请求分布。
误区四:跨节点 TP 和单节点 TP 一样配置
跨节点 TP 对网络非常敏感。没有 InfiniBand、RoCE、GPUDirect RDMA 或正确 NIC 亲和性的情况下,跨节点 Tensor Parallel 可能产生明显尾延迟抖动。很多场景下,单节点 TP + 多副本 DP 比跨节点 TP 更稳定。
适用场景
适合使用 Tensor Parallel 的场景:
- 单卡显存无法容纳模型权重和目标 KV cache
- 需要服务 70B、100B 以上模型,且节点内有高速 GPU 互联
- 单副本需要更高 decode 吞吐,且业务能接受一定跨卡通信开销
- 需要结合 Pipeline Parallel 运行单节点放不下的大模型
不适合优先使用 Tensor Parallel 的场景:
- 模型单卡已经稳定,瓶颈只是请求量大
- 节点没有高速互联,PCIe 拓扑复杂
- 请求多为短上下文、短输出,通信开销占比高
- 团队还没有 NCCL、GPU 拓扑、RDMA、压测和故障恢复经验
上线检查清单
上线前至少确认以下事项:
- 模型版本、量化版本、
max_model_len、TP/PP/DP 配置已固化 - 已记录 GPU SKU、显存、NVLink/NVSwitch/PCIe 拓扑和 NIC 位置
- 已完成短输入、长输入、短输出、长输出的压测矩阵
- 已分别记录 TTFT、TPOT、P95/P99、tokens/s、request/s
- 已检查 vLLM 日志中的 GPU KV cache size 和 Maximum concurrency
- 已验证 NCCL 使用期望网络路径,没有意外 socket fallback
- Kubernetes 节点标签、GPU 资源、IPC_LOCK、/dev/shm、RDMA 条件已纳入部署模板
- 灰度阶段保留 NCCL、GPU、网络和服务层日志
- 已定义 NCCL timeout、rank hang、GPU OOM、节点重启时的自动摘除与回滚策略
- 已准备降级方案:降低
max_model_len、减少并发、切换小模型、回退单节点 TP 或多副本 DP
参考资料
- vLLM Parallelism and Scaling
- vLLM Engine Arguments
- Hugging Face Transformers: Tensor Parallelism
- NVIDIA NCCL Overview
- NVIDIA NCCL Collective Operations
- TensorRT-LLM Useful Build-Time Flags
- gLLM: Global Balanced Pipeline Parallelism System for Distributed LLM Serving with Token Throttling
- Parallel Track Transformers: Enabling Fast GPU Inference with Reduced Synchronization