张量并行度的选择与通信收益拐点

在一组 8 卡 ****(单卡 64GB HBM,卡间互联 392 GB/s)上,用 **** 的基础软件栈配合 vLLM 起一个 70B 级 BF16 模型服务,把 --tensor-parallel-size 从 1 逐档提到 8。每卡权重占用随档位下降,--max-model-len 能开到更高,但 decode 吞吐在某一档之后不再上升,再往上加卡反而低于前一档;启动日志无异常,resolved_backend 与基线一致,没有算子回退。问题集中在两处:每层前向要付的 all-reduce 通信量不随 TP 度下降,而 all-reduce 的耗时随参与卡数上升。

张量并行切的是什么

张量并行按维度切分同一层内的张量,不把层分给不同卡。被切的对象有三类:

被切对象 切分维度 每卡占比
注意力投影矩阵(Q/K/V/O) 按注意力头切,每个头完整落在一张卡上 约 1 / tensor_parallel_size
MLP 的列并行与行并行矩阵(上、下投影) 列投影按输出维切,行投影按输入维切 约 1 / tensor_parallel_size
KV 头 按 H_kv 切,每卡持有 H_kv / tensor_parallel_size 个 KV 头 约 1 / tensor_parallel_size
词表相关层(embedding、LM head) 按词表维切 约 1 / tensor_parallel_size

注意力头按卡切分要求 tensor_parallel_size 能整除头数;GQA/MQA 下 KV 头数远小于 query 头数,KV 头往往无法整除同样的档位,框架会改用张量复制或退化切分,读日志时应以实际生效的切分形状为准。对采用 MLA 的结构,KV 被压缩为潜向量,H_kv 与单头维度 D 的口径与 GQA 不同,不能直接套用注意力头数。

权重按参数量线性摊到各卡上,每卡权重下限可写成:

每卡权重下限 ≈ 参数量 × 每参数字节数 ÷ tensor_parallel_size

量级示例(估算,非实测):70B 参数、BF16(每参数 2 字节)时单卡需约 140 GB,TP=8 时每卡约 17.5 GB。显存查询工具报的是整卡剩余量,不是权重基线所需量。

每层前向要付的通信

张量并行在每层引入固定次数的 all-reduce。以标准 decoder 层为例,一次前向付两次:

  1. attention 的输出投影(O 矩阵)之后一次,把各卡分头计算的部分结果求和;
  2. MLP 的下投影(行并行矩阵)之后一次,把各卡按输入维切分的部分结果求和。

列并行矩阵之后不需要 all-reduce,因为输出维本来就按卡分开;行并行矩阵之后必须 all-reduce,因为每张卡只算到了部分和。层数越多,付的次数越多。

每层 all-reduce 次数 = 2
单次 all-reduce 数据量 ≈ batch × seq × hidden × 每元素字节数   # 与 TP 度无关
ring all-reduce 单卡通信量 ≈ 2 × (N-1)/N × 单次数据量           # N 为 TP 度

两处要点:单次 all-reduce 的数据量由激活张量大小决定,调大 tensor_parallel_size 并不会让它下降;而 all-reduce 的耗时包含一个随参与卡数增长的项,卡越多、跨的链路层级越高,这一项越突出。计算时间随 TP 度按 1/N 下降,通信时间随 N 上升,两条曲线相交处就是收益拐点。

拐点由互联带宽决定

拐点位置由计算时间与通信时间的比决定,而通信时间取决于互联带宽。同一拓扑下带宽越高,通信项越平,TP 度可以推到越高才触顶。

互联层级 单卡互联带宽(示意量级) 适合承载的并行
机内全互连总线 数百 GB/s 至 1.8 TB/s 张量并行(每层都要通信,需要最高带宽)
PCIe 点对点 约 100–128 GB/s 低 TP 度或对带宽不敏感的切分
跨机以太网 / RDMA 低于机内一个量级 张量并行收益急剧下降,应改用管线或数据并行

提高 TP 度只在权重放不下时才带来净收益。显存不足以容纳权重时,多卡分摊让模型起得来;权重放得下之后,继续加卡的收益由互联带宽与拓扑层级封顶,不再提高吞吐。

现象

同一处预算不足会以几种形态出现:

定位步骤

  1. 确认实际生效的并行度与切分形状:读启动日志里的并行配置行与权重的实际分片形状,核对 tensor_parallel_size 是否被整除约束改写。部分框架把张量并行数记在 worldSize 字段,字段名与含义须按实际输出对照。
  2. 分段计时:分别取 TTFT 与 TPOT / ITL,判断吞吐下降落在 prefill 段还是 decode 段。decode 段对通信更敏感,因为是逐 token 反复付 all-reduce。
  3. 测通信占比:用 profiler 抓一段 decode,统计集合通信算子的等待时间与计算算子时间之比;跨机时把 all-reduce 单独拆出来计时。
  4. 看互联带宽利用率:按「带宽利用率 = 网络带宽实际使用量 ÷ 网络带宽」读链路计数器,利用率长期贴近上限说明受带宽约束,利用率很低却仍慢说明受固定延迟或同步点约束。
  5. 扫 TP 度画曲线:固定模型、精度与负载,把 tensor_parallel_size 从 1 逐档扫到 8,记录每档的 decode 吞吐。曲线由升转平、再转降的那个点就是本机拐点。
# 示意:定位时固定 TP 度的启动命令(参数名取自框架,取值按本机规格填)
vllm serve <model> \
  --tensor-parallel-size 4 \
  --max-model-len 32768 \
  --gpu-memory-utilization 0.9 \
  --kv-cache-dtype auto \
  --max-num-seqs 64

处置:参数与代价

动作 作用 代价 / 前提
调低 --tensor-parallel-size 到刚好放得下 通信项随卡数下降,拐点前区间吞吐回升 每卡权重上升,可能装不下更大模型或更长上下文
KV cache 转 fp8 每卡 KV 占用减半,用更少卡即可放下 需目标 attention backend 支持该 dtype,否则失格回退
权重做 W8A8 / W4A16 量化 每卡权重下降,可少用卡 引入量化误差,需单独核精度
换更高带宽的机内全互连 抬高拐点,让更大 TP 度仍有利可图 受硬件与拓扑约束,属机内档位
把 TP 限制在机内、跨机用管线并行 避免跨机付每层 all-reduce 的固定延迟 引入管线气泡,见并行组合的取舍
开 --enable-chunked-prefill 压平 prefill 峰值,给通信让出重叠空间 调度更复杂,须确认后端支持

调整顺序建议:先确认模型在更低 TP 度下放得下,放得下就不要再加卡;放不下再考虑 KV dtype 与权重量化,把每卡占用压下来;只有当显存确实不够、且机内拓扑允许时,才把 TP 度往上推。

记录字段

# 示意:一条张量并行度结论的记录(字段名示意)
model:
  params:              # 参数量,用于反推每卡权重下限
  precision:           # FP16 / BF16 / W8A8 / FP8 / W4A16
hardware:
  card_count:          # 卡数(8 卡,单卡 64GB HBM)
  interconnect:        # 机内全互连 / PCIe / 跨机 RDMA,注明单卡互联带宽
parallelism:
  tp_size:             # tensor_parallel_size 实际生效值
  world_size:          # 若框架把张量并行数记在此字段,注明口径
workload:
  input_len:
  output_len:
  max_num_seqs:
perf:
  ttft:
  tpot:
  decode_throughput:   # 逐 TP 度扫描后的曲线取值
comm:
  allreduce_ratio:     # 集合通信等待时间占 decode 的比例
  link_utilization:    # 互联带宽利用率
status:                # 拐点前 / 拐点后;注明结论在哪种负载下成立

小结

张量并行按维度切分注意力头、MLP 投影与 KV 头,每卡权重下限为参数量乘每参数字节数再除以 tensor_parallel_size。每层前向固定付两次 all-reduce,单次数据量由激活张量大小决定、不随 TP 度下降,而 all-reduce 的耗时随参与卡数上升,两条曲线相交处即收益拐点,拐点位置由互联带宽与拓扑层级决定。遇到「每卡显存够了、吞吐却不升反降」时,先分段计时与测通信占比,再按机内带宽与每卡占用决定是把 TP 度调低,还是换 KV dtype 与量化把占用压下来。