2218 字
约 7 分钟
17
S23-A24 · 加卡没变快:算力、显存与带宽的三笔账

一个推理服务从 2 卡扩到 8 卡,期望吞吐翻四倍,实际只涨了 1.6 倍。查下来三个原因叠在一起:单请求的 KV 缓存放不进"单卡"(于是"跨卡通信"吃掉了收益)、调度器把"批"拆到了每张卡(于是 batch 变小)、以及模型副本数没变(8 卡跑 2 个副本,其余在等)。

"加卡"这件事,收益取决于"你缺的是什么":算力、显存、还是卡之间的带宽。这个"缺什么"决定了"要不要切模型":缺算力(并发不够),加副本就行;缺显存(模型放不下),才必须"切模型"——而"切模型"几乎总带通信。
而"缺"的那一项,决定了"该怎么切"。

1. 三种"瓶颈"的判别

瓶颈 现象
算力 GPU 利用率高、吞吐随卡数近线性
显存 批上不去、OOM、KV 不足
卡间带宽 利用率低、卡在 "all-reduce"

第一项是"最理想"的:加卡真的能变快——而第二、三项会让"加卡"变成"加复杂度"。第二项与第三项的"代价"是"非线性":加卡越多,通信占比越高——于是"2→4 卡"还行,"4→8 卡"可能只有 1.3 倍。

"加卡前后"的三个数,能一次定位"收益去哪了":

// 三个数(同一负载,2 卡 vs 8 卡)
每卡 GPU 利用率:2 卡 85% → 8 卡 35%   → 说明"批被切碎"
卡间通信占比:  2 卡 5%  → 8 卡 45%    → 说明"TP 通信吃收益"
每卡 batch:    16       → 4           → 说明"调度把批拆了"
// 判据:三个数至少一个"恶化" → 先修它,再谈"加卡"

2. "为什么加卡没变快"的三种情形

情形 机制
模型"放不进单卡"(张量并行) 每层要"跨卡同步"
副本数没增加(只加卡) 并行度不变
调度把它"切碎" 每卡 batch 变小

第一项的"量级"值得记:卡内带宽(HBM)是 TB/s 级,卡间 NVLink 是百 GB/s 级,PCIe 只有几十 GB/s——差一到两个数量级,于是"通信占比"很容易"超过 50%"。
第一项是"通信换显存":张量并行把一层拆到多卡——于是"每算一层"都要"跨卡 reduce",而"卡间带宽"远低于"卡内带宽"。

"副本与路由"是"最便宜的扩容",而它只要"调度器支持":

// 三种"扩副本"的配置(同一集群)
replicas: 4                 # 每卡一副本(模型放得下时最优)
routing: least_pending      # 路由到"最闲"的副本(避免忙闲不均)
batch_per_replica: 16       # 每副本的批大小(别让调度"按卡切")
// 判据:加副本后"每卡利用率"应保持,而不是"集体下降"

3. "三种并行"的适用

并行 切什么
数据并行(DP) 批(每卡一份模型)
张量并行(TP) 层内(一层拆多卡)
流水并行(PP) 层间(不同层放不同卡)

第一项是"最省心"的:**每卡一份完整模型,卡间只"传梯度"**第一项的"推理版"更简单:每卡一份模型,各卡独立处理各自请求——卡间"零通信",于是"吞吐随卡数近线性"(只要"路由均匀")。
——推理场景甚至"零通信"(各卡独立服务)。

"线性度"是个可算的验收指标,而它比"感觉快"可靠:

// 加卡线性度 = 吞吐倍数 / 卡数倍数
2→4 卡:吞吐 1.9 倍 → 线性度 95%(好)
4→8 卡:吞吐 1.3 倍 → 线性度 65%(差:通信或调度)
// 判据:线性度 < 80% 就"别再买卡",先修"通信/批大小"

4. "副本与并行"的取舍

方案 前提
多副本(DP) 模型放得进单卡
TP(一层拆卡) 单卡放不下,且"卡间带宽高"(NVLink)
PP(层拆卡) 层数多、单卡放不下

第二项的"判据"很直接:"TP 度 ≤ 单机 NVLink 域内的卡数"(如 8 卡 NVLink 域,TP 到 8 都还行;跨机 TP 基本不建议)。
第二项的"边界"很硬:"卡间走 PCIe"时,TP 的收益常被通信吃掉——于是"8 卡 PCIe 做 TP"可能比"2 卡 NVLink"更慢.

"KV 缓存"是"显存账"里最容易被漏的一项:

// 三段"显存占用"(同一模型)
权重:   14GB(TP=2 时每卡 7GB)
KV:     与"并发 × 序列长度"成正比(可能 8GB)
激活:   与"batch"成正比(1–2GB)
// 判据:TP 只切"权重",KV 与激活"仍按卡算"——所以"长上下文"要单独"KV 分片"

5. "调度"对吞吐的影响

现象 机制
每卡 batch 变小 调度按"卡"切请求
副本"忙闲不均" 路由不均
"等待同步" TP 的每层屏障

第一项是"加卡后最常见"的:**原来"1 卡 batch 16",变成"8 卡各 batch 2"**第一项的"机制"是"每卡的 batch 决定"矩阵乘的利用率"":batch 2 时,GPU 的算力大量闲置——于是"卡多了、总 batch 没变",吞吐自然不动。
——于是"每卡每批"的利用率下降,而"总吞吐"未必上升.

6. 排错:五种"加卡无收益"

现象 成因 处理方向
吞吐涨不到线性 通信/调度 先量"卡间通信占比"
GPU 利用率低 批太小 调大"每卡批"
延迟反而变差 TP 通信 减小 TP 度,改多副本
OOM 仍在 并行没覆盖到 KV 加"KV 分片"
有卡"闲着" 副本数/路由 加副本或改路由

"延迟敏感"这条要单独强调:TP 会让"每层多一次通信",于是"单请求延迟"上升。

// 两种"扩"的延迟影响
多副本:单请求延迟不变(只是"更多请求能并行")
TP:   单请求延迟上升(每层都要等"最慢那张卡")
// 判据:SLO 是"P99 首字"还是"总吞吐"

7. 边界:什么情况"多卡"不值得

场景 为什么 做法
模型放得进单卡 多副本已够 加卡 = 加副本
低流量(QPS < 1) 并行度用不上 单卡
PCIe-only(无 NVLink) TP 通信太贵 避免 TP
延迟敏感(单请求 <1s) TP 增加每层延迟 多副本优先

最后一条与"验收"有关的边界:"加卡前后"要量"每卡利用率 + 卡间通信时间占比 + 每卡 batch"三个数——三个数一起看,才知道"收益被谁吃掉了";只看"总吞吐",永远只能猜。
判断口径一句话:"我要的是"更多并发"还是"更大模型"。要并发(模型放得下),加卡=加副本,别碰 TP;要更大模型(放不下),才用 TP/PP,且要先确认"卡间带宽够不够"。

再补一个"看起来能省"的坑:"用更多卡跑更小模型"有时不如"少卡跑原模型"——因为"小模型"的算力利用率本来就低(batch 小、矩阵乘窄),而"多卡并行"要付"通信"的固定成本。于是"加卡 + 换小模型"的组合,可能"总成本不变、延迟变差"。而判断它的方式还是那三个数:利用率、通信占比、每卡 batch——没有哪一项因"换小模型"而变好,就不该换。

最后一个"决策"提醒:"加卡"之前先回答"瓶颈是哪一项"——答不出,就先"量三个数"。因为"加卡"是"最贵"的扩容手段(钱、机位、运维),而它"解决不了"显存与通信;而"加副本 + 调批大小"往往能"用同样的卡"拿到一半收益。

所以"先量后买",是这类扩容唯一靠谱的顺序。

8. 自测

  1. 三种瓶颈分别有什么现象?为什么"加卡真的能变快"属于最理想的情况?
  2. "张量并行"为什么会"用通信换显存"?卡内带宽与卡间带宽差多少量级?
  3. 三种并行分别"切"什么?为什么"推理场景的数据并行"几乎零通信?
  4. 为什么"8 卡 PCIe 做 TP"可能比"2 卡 NVLink"更慢?
  5. "每卡 batch 变小"怎么会让"总吞吐"不升反降?
  6. 判断"该多副本还是该 TP"的那个问题是什么?
S23-A24 · 加卡没变快:算力、显存与带宽的三笔账
https://www.clxhxhhr.top/posts/6679/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。