📚 双 RTX 5060 Ti 部署 Qwen3.8 系列 · 3/6
💡 导读:
TP2 能让两张 GPU 同时参与同一层,但双卡并不会因此稳定得到 2 倍 token/s。这篇用 33.5 token/s 的真实结果、无 CUDA P2P 的机器环境,以及跨卡拷贝和 AllReduce 数据,把“PCIe 会不会拖垮 TP”这笔账拆开来看。
上一篇讲了 TP:两张 GPU 同时参与同一层的计算。
既然 RTX 5060 Ti 16GB 单卡显存带宽约为:
448 GB/s
那么双卡的物理显存带宽总和就是:
448 × 2 = 896 GB/s
一个很自然的问题是:
既然 TP2 能让两张 GPU 同时读取自己的权重分片,为什么单流 Decode 不能稳定获得 2 倍 token/s?
真正的答案不是一句“PCIe 太慢”就能解释的。
TP2 的最终收益更接近:
并行计算与显存带宽收益
-
跨卡通信
-
同步延迟
-
Kernel / 调度额外开销
=
最终 Scaling
而这次实测有一个很有意思的地方:
2 × RTX 5060 Ti 16GB
Qwen3.8-27B NVFP4
TP2
单流 Decode ≈ 33.5 token/s
更特别的是,这台机器甚至没有 CUDA P2P。
1. 从显存带宽看 TP2 的理论收益
一次 LLM 请求大致可以分成:
Prefill
处理 Prompt
Decode
逐 token 生成
Prefill 一次处理大量 Token,矩阵规模较大,Tensor Core 通常更容易吃起来。
单请求 Decode 则不同。
每一步只生成一个 Token,但这个 Token 仍然要经过整个模型的大量权重:
Token N
↓
Layer 0
↓
Layer 1
↓
...
↓
Layer 63
↓
Token N+1
所以单流 Decode 经常表现出明显的 Memory Bound 特征。
可以用一个非常粗糙、但很适合建立直觉的公式:
Decode token/s
≈
显存有效带宽
÷
每 token 需要处理的权重数据量
假设量化后的 27B 模型文件大约 23GB。
单张 5060 Ti:
448 GB/s ÷ 23GB ≈ 19 token/s
这个数字当然不是严谨 Benchmark。
模型文件大小并不严格等于每颗 token 的实际 DRAM Traffic,实际性能还会受到:
- Kernel
- 量化格式
- Tensor Core
- Cache / State
- 模型结构
- Batch
- Attention Backend
等因素影响。
但这个估算能先建立一个重要直觉:
单流 Decode 里,显卡的峰值 TFLOPS 并不是唯一重要指标;能多快地把权重和状态送进计算单元同样关键。
TP2 为什么理论上更容易聚合双卡带宽?
如果是按层切:
GPU0
处理前半模型
↓
GPU1
处理后半模型
对于同一颗 token,两部分存在明显的前后依赖。
所以虽然每张卡都有 448GB/s,但不能简单理解为始终同时提供 896GB/s。
TP2 则不同。
一层里的权重被切成两份:
Layer N
GPU0 GPU1
权重 Part A 权重 Part B
↓ ↓
计算 计算
└──────┬────────┘
↓
同步
两张卡可以同时读取自己的权重分片。
因此从显存带宽角度,TP2 的确更接近:
448 + 448 ≈ 896 GB/s
问题就变成了:
每层为了同步而增加的通信,会不会把这部分收益全部吃掉?
实测 33.5 token/s:和粗略带宽模型处在同一量级
这次双卡 TP2 的单流 Decode 实测大约是:
33.5 token/s
模型权重大约 23.19GB。
如果只为了建立一个“理想带宽上限”的直觉,可以做:
896 GB/s ÷ 23.19GB ≈ 38.6 token/s
而实际是:
≈ 33.5 token/s
这两个数字处在同一个量级,至少说明一件事:
TP2 确实获得了很明显的双卡并行收益,并没有因为跨卡通信直接退化成接近单卡的状态。
但这里我不会再把 33.5 / 38.6 解释成某个精确的“显存带宽利用率”。
原因很简单:
23.19GB checkpoint size ≠ 每 token 精确 DRAM 读取量
NVFP4 的 scale、部分高精度 tensor、Cache、量化 Kernel、Activation 以及其他计算都不在这个极简模型里。
所以这个公式只能告诉我们:
实测结果与“TP2 聚合了相当一部分双卡权重读取能力”这个解释是一致的。
它不能用来精确计算真实显存带宽利用率,也不能把理论值和实测值之间的全部差距都归因于 PCIe。
2. 没有 P2P,TP2 为什么仍然有效?
测试服务器上的 CUDA P2P 检查结果是:
can_device_access_peer = False
也就是说,不能按照最理想的:
GPU0 ←──────→ GPU1
P2P
去理解它的跨卡路径。
这台机器上实际做过的通信测试大致是:
大块跨卡拷贝:≈ 6.7 GB/sAllReduce 基准:≈ 7.8 GB/s
这两个值来自不同测试,不能直接互相替代,但数量级都远低于单张 GPU 的:
448 GB/s
第一眼看上去问题非常严重:
GPU 本地显存:几百 GB/sGPU 间通信:个位数 GB/s
为什么 TP2 还能跑到 33.5 token/s?
答案在于:
TP 并不会在两张 GPU 之间传整个模型。
权重一直在各自 GPU 上,跨卡传的是中间结果
TP2 下,两张 GPU 已经分别保存好了自己的权重分片:
GPU0
一直保存 W0
GPU1
一直保存 W1
每生成一颗 token 时,并不会发生“GPU0 把十几 GB 权重传给 GPU1”这种情况。
真正需要跨 GPU 同步的,主要是:
- Activation
- Hidden State
- 部分中间结果
这些数据量比整个模型小得多。
以这次模型的 hidden size:
hidden_size = 5120
假设中间状态用 BF16:
5120 × 2 Byte ≈ 10 KiB
一个 hidden vector 只有大约 10KiB。
而模型权重是二十多 GB。
这两个数据量完全不是一个数量级。
粗略估算一下 TP2 每 token 的 Collective 数据量
对于典型 Transformer TP,可以做一个数量级估算:
- Attention 后一次主要 Collective
- MLP 后一次主要 Collective
如果粗略按:
64 Layers
×
2 次 / Layer
≈
128 次 Collective / token
来建立直觉。
TP2 下,一个约 10KiB 的 BF16 hidden vector,在典型 AllReduce 中每个 rank 传输的数据量也是同一数量级。
于是:
128 × 10 KiB ≈ 1.25 MiB / token
注意,这不是对当前 vLLM 所有 Kernel 和通信操作做出的精确 Trace,只是用来估算数量级。
它想说明的是:
每 token 处理的权重:几十 GB 级每 token 跨卡同步的数据:MB 级
这就是为什么:
PCIe 带宽远低于 GPU 本地显存带宽,并不等于 TP 一定会被 PCIe 卡死。
只用“数据量 ÷ 7.8GB/s”也会低估通信成本
如果非常粗暴地拿:
1.25 MiB / token ÷ 7.8 GB/s
得到的纯传输时间只有大约零点几毫秒。
而:
33.5 token/s
对应每颗 token 总时间约:
1000 ÷ 33.5 ≈ 29.9 ms
看起来通信似乎几乎可以忽略。
但这里又会进入另一个误区。
TP Decode 的通信不是一次连续传 1.25MiB,而更像:
算一点
↓
同步一个小消息
↓
再算一点
↓
再同步
↓
重复很多次
所以真实通信成本更接近:
通信时间
=
数据传输时间
+
Collective latency × Collective 次数
而不是简单的:
总字节数 ÷ 大块带宽
TP Decode 更应该担心“小消息延迟”
前面的 6.7GB/s、7.8GB/s,更多是在描述较大数据块下能够达到的吞吐。
但单 token TP 的通信经常是大量小消息和同步点。
假设一颗 token 要经历一百多次 Collective,那么即使每次数据很小:
启动、同步和等待延迟也会累积。
NCCL 也会根据:
- 拓扑
- 消息大小
- GPU 数量
- Collective 类型
选择不同算法。
所以“大块 memcpy 带宽是多少”,不能直接拿来预测 LLM TP Decode。
这也是为什么最终最靠谱的方法还是:
通信微基准 + 真实模型 Benchmark 一起看。
3. 33.5 token/s 背后的真实损失
现在整个模型就比较清楚了。
TP2 得到的收益是:
收益来自两部分:两张 GPU 同时读取各自权重分片,以及两张 GPU 同时执行同一层的部分计算。
付出的成本则包括:
Collective latency
PCIe 数据传输
同步
TP 调度
更小的 GEMM Shape
Kernel 效率损失
另外还存在:
- 显存带宽不可能永远 100% 有效
- NVFP4 量化与 scale 处理
- Attention / State 计算
- 其他 Runtime 开销
所以更准确的说法是:
TP2 聚合了相当一部分双卡显存带宽和计算能力,但不会获得理想 2× Scaling。
33.5 token/s 对这套消费级双卡、无 P2P 环境来说,已经说明 TP2 是有效的,而不是理论上好看、实际被 PCIe 拖垮。
GPU Util 99% 也不等于 Tensor Core 跑满
Decode Benchmark 时,两张 GPU 的 Util 可以接近:
99%
这个现象很容易被理解成:
“GPU 算力已经跑满,所以瓶颈一定是纯计算。”
但 nvidia-smi 的 GPU Util 并不是 Tensor Core 利用率。
它更接近表示:
采样周期内 GPU 是否持续存在正在执行的工作。
所以:
GPU Util = 99%
不能推出:
Tensor Core = 99%
也不能直接证明已经从 Memory Bound 变成 Compute Bound。
GPU 完全可能一直在忙,但相当一部分时间仍然受显存读取、小 GEMM 或同步影响。
不过至少这次没有看到一种更糟糕的状态:
通信等待让 GPU 大量时间完全空闲。
4. P2P、NVLink 和 PCIe 不要混成一个概念
这几个词经常被混在一起。
NVLink
GPU 专用高速互联。
RTX 5060 Ti 没有 NVLink。
PCIe
GPU 与主机、以及多 GPU 通信路径的重要底层总线体系。
没有 NVLink,多 GPU 仍然可以通过 PCIe 体系完成通信。
CUDA P2P
指一个 GPU 是否能够直接访问另一张 GPU 的显存。
它不仅和显卡型号有关,还受到:
- PCIe 拓扑
- Root Complex
- 主板 / 平台
- 虚拟化环境
- 驱动与平台配置
影响。
所以:
没有 NVLink ≠ 一定没有 P2P
而:
有 PCIe ≠ 一定支持 CUDA P2P
这次租用环境属于:
没有 NVLink
+
P2P 不可用
+
TP2 仍然有效
反而很适合说明:
判断 TP 是否值得,不能只看“有没有 P2P”一个开关。
5. TP2 有收益,不代表 TP 越大越好
从 TP2 继续扩大到:
TP4 / TP8
会同时发生几件事。
每张卡权重更少
这是好事。
TP2:每卡大约 1/2TP4:每卡大约 1/4
单卡需要读取和计算的权重继续减少。
但 GEMM 也会被切得更小
单 token Decode 本来就是小 Batch / 小 GEMM 场景。
继续切矩阵以后,每张卡拿到的 Shape 更小,GPU 不一定还能维持同样高的执行效率。
Collective 参与者更多
2 卡
→
4 卡
→
8 卡
同步拓扑和延迟都会变复杂。
所以 Scaling 往往更接近:
TP1 → TP2
收益明显
TP2 → TP4
可能继续提升,但效率下降
TP4 → TP8
越来越依赖硬件互联与 Kernel / Collective 实现
这也是消费级 PCIe 多卡和 NVLink / NVSwitch 平台的差距,会随着 TP Degree 增大而越来越明显的原因之一。
双 5060 Ti 的实际结论
这次实测可以总结成:
2 × RTX 5060 Ti 16GB
Qwen3.8-27B NVFP4
TP2
单流 Decode ≈ 33.5 token/s
同时机器:
没有 NVLink
CUDA P2P 不可用
大块跨卡拷贝 ≈ 6.7 GB/s
AllReduce 基准 ≈ 7.8 GB/s
在这种并不理想的跨卡条件下,TP2 仍然获得了明显的单流 Scaling。
原因并不神秘:
收益:
两张 GPU 同时读取自己的权重分片
成本:
同步 MB 级中间状态
+
大量小消息 Collective latency
所以我现在对消费级双卡 TP 的判断是:
没有 NVLink,不应该直接否定 TP。
甚至:
P2P 不可用,也不能单独证明 TP 不值得跑。
真正应该做的是:
测单卡 / TP2 token/s
测实际跨卡通信
看 GPU 拓扑
估算 Collective 数量级
最后用真实 Scaling 判断
6. 结论
双卡推理没有两倍快,不是因为一句:
PCIe 太慢
就能解释。
TP2 实际上是两股力量的竞争:
双卡显存带宽与计算并行
↑
│
最终性能
│
↓
PCIe / Collective / 同步 / Kernel 开销
在这次双 5060 Ti + 27B 的实测中,TP2 的结果说明:
消费级 PCIe 双卡依然可以通过 TP 获得很有价值的单流加速。
下一步问题就从“TP 到底有没有用”变成了:
既然模型已经能放下、TP 也确实能加速,真正做长期推理服务时,llama.cpp 和 vLLM 到底应该选谁?
下一篇:
《为什么生产推理我默认 vLLM:从 NVFP4、Kernel 到 CUDA Graph》
系列导航: ← 上一篇:双卡部署大模型:TP、PP、DP 和 Layer Split 到底有什么区别? · 下一篇:为什么生产推理我默认 vLLM:从 NVFP4、Kernel 到 CUDA Graph →