HBM 显存架构与带宽:从 HBM3e 到训练/推理带宽瓶颈
说明:本篇为综述/无第一手实验数据,采用公开技术资料与架构解析路线。
实验目标
为什么 2023 年之后的大模型训练,大家都在反复强调"显存带宽"而不是"显存容量"?答案藏在算力与带宽的增速剪刀差里:每一代 GPU 的 FP16 算力大约翻倍,而显存带宽只增长 40%-60%。以 H100 为例,BF16 稠密算力约 989 TFLOPS,HBM3 带宽约 3.35 TB/s——如果让计算单元"吃饱",每个字节的算术强度必须达到约 295 FLOP/B;而 Transformer 训练中相当比例的操作(注意力、归一化、梯度同步、参数加载)算术强度远低于这个值。本篇的目标是建立一个清晰的带宽核算框架:给定模型与集群配置,先算带宽够不够,再谈算力够不够,并给出可落地的排查与调优清单。
代码逐段拆解
先看一段经典的"带宽核算"脚本。它用 PyTorch 的显存拷贝来测量可达带宽,这是判断"配置是否够用"的第一步:
import torch, time
def measure_bw(shape, iters=100):
a = torch.randn(shape, device='cuda', dtype=torch.float16)
b = torch.empty_like(a)
# warmup:驱动与 CUDA context 初始化
b.copy_(a); torch.cuda.synchronize()
t0 = time.perf_counter()
for _ in range(iters):
b.copy_(a)
torch.cuda.synchronize()
dt = (time.perf_counter() - t0) / iters
bytes_moved = 2 * a.numel() * a.element_size() # 读 + 写
return bytes_moved / dt / 1e12 # TB/s
# 32GB 显存的 H 级卡:32*0.9 可分配 ≈ 28GB
print(measure_bw((6_000_000_000,), iters=50), "TB/s 实测拷贝带宽")逐段理解:
copy_是带宽探针:一次copy_要读整个张量再写整个张量,移动字节数 =2 * numel * element_size,实测值接近理论带宽的 80%-90%(受 ECC、内存控制器开销影响)。这个数字就是后面一切估值的"基准带宽"。warmup必须做:第一次 CUDA 调用要初始化 context、分配流处理器,不预热会把首次延迟算进带宽,造成假低值。synchronize卡点:GPU 是异步的,不同步就计时,循环会全部入队后瞬间返回,测出假高值。- 用
numel * element_size而不是nbytes的差值:内存分配的对齐与页大小会造成nbytes虚高,按元素算更准确。
原理讲解
从 GDDR 到 HBM:为什么带宽成了稀缺资源
GDDR(如 GDDR6X)靠提高 I/O 频率堆带宽,代价是功耗高、PCB 走线长;HBM(High Bandwidth Memory)换了一条路——把内存 die 垂直堆叠,用硅中介层(interposer)短距离互连。堆叠解决容量(层数×单 die 容量),短互连解决带宽(走线短→可以加宽数据总线)。HBM3e 每个伪通道(pseudo-channel)独立编址与调度,一个堆叠有 8-16 个伪通道,访存并行度大幅提升。
关键数字对比(公开资料口径):
| 指标 | GDDR6X (RTX 40 系) | HBM3 (H100) | HBM3e (H200) | HBM3e 12-Hi (B200) |
|---|---|---|---|---|
| 单卡带宽 | ~1 TB/s | 3.35 TB/s | 4.8 TB/s | 8 TB/s |
| 单堆叠容量 | — | 16GB | 24GB | 36GB |
| 位宽 | 384-bit | 5120-bit | 5120-bit | 8192-bit |
(数值为厂商公开规格,估算口径。)
带宽为什么卡住大模型
以 70B 参数、BF16(140GB 权重)模型为例:
- 推理逐 token 生成:每个 token 都要把全部权重从 HBM 读一遍,算术强度 ≈ 每 token 的 FLOPs / 权重字节数,通常只有几十 FLOP/B,远低于 GPU 的峰值算术强度。因此推理的 token 吞吐 ≈ 带宽 / 权重体积:H100 上约 3.35TB/s ÷ 140GB ≈ 24 token/s(单卡、单请求)。H200 带宽 4.8TB/s,同样模型直接提到 ~34 token/s——这就是"换 H200 推理更快"的底层原因。
- 训练中的注意力与 RMSNorm:这两类算子访存密集(按 token 读 K/V、读统计量),不是算力瓶颈而是带宽瓶颈,所以 FlashAttention 的核心工作就是把"读 HBM → 算 → 写 HBM"改成"读一次 SRAM → 算完 → 只写回结果"。
带宽核算框架
对任意配置,先按以下顺序核算:
- 权重加载带宽:
推理吞吐 ≈ 可用带宽 ÷ 单卡权重字节(KV Cache 未命中场景)。 - 激活访存:训练中激活流量 ≈
batch_size × 序列长 × 隐藏维 × 层数 × 每层读写次数 × 字节数,用张量并行切分后按卡分摊。 - 通信带宽:AllReduce 流量在计算网络(ROCE)上走,不走 HBM——但梯度张量要先写回 HBM 再被 RDMA 读取,显存带宽不足会拖慢通信启动。
- 对比判定:若
模型总 FLOPs / 显存总带宽 > 单卡算力 / 单卡带宽 × 卡数,说明系统处于带宽受限区,加卡不加带宽收益递减。
实验结果
用上述框架做三个典型估算(估算口径,供选型参考):
| 场景 | 配置 | 带宽估算 | 结论 |
|---|---|---|---|
| 70B 推理(单卡 H100) | 140GB BF16 | ~24 token/s | 换 H200 → ~34 token/s,带宽直换吞吐 |
| 70B 推理(H100 × 8 张 TP) | 每卡 17.5GB | 带宽按卡分摊,权重每卡 1/8 | 吞吐≈单卡×8,但受 AllReduce 通信限制 |
| 7B 微调(单卡 A100 40G) | 权重 14GB + 优化器态 | 带宽受限阶段:Adam 态读写在 1.5TB/s 上占大头 | 优先用 ZeRO/FSDP 减显存占用,而非加卡 |
(以上为基于公开规格的估算,非实测数据。)
工程启示
- 先量带宽,再谈算力:任何训练/推理系统上线前,先跑一遍上面的
measure_bw得到"可达带宽"基线,把它写进硬件验收清单,与厂商标称值对比(一般 80%+ 为正常)。 - 推理选型看 HBM 带宽密度:同算力下,HBM 带宽更高的卡对长上下文和 LLM 生成更友好;"GB/s 每美元"比"TFLOPS 每美元"更贴近真实推理成本。
- 把 HBM 当稀缺资源设计:训练脚本里控制 activation checkpointing 的粒度、通信张量是否常驻显存、KV Cache 预留水位——每一笔显存流量都会挤占带宽。
- 监控带宽而不只监控利用率:
nvidia-smi dmon -s m里的 fb 利用率只是带宽的粗略代理,要精确看带宽需用 DCGM 或 Nsight Systems 的 memory workload 指标(下一篇会展开集群级可观测性)。
下一篇预告:本篇回答了"单卡带宽怎么看",下一篇进入集群视角——"GPU 集群可观测性:DCGM 监控体系与告警设计",讲清楚集群里每张卡的功耗、温度、带宽、SM 占用如何被统一采集与告警。