硬件基础(二):内存层次结构与互联技术

聊完了计算单元,咱们得聊聊数据怎么喂给这些计算单元。你想想看,GPU 算得再快,数据堵在路上也是白搭。这就像我当年调试一个 8 卡训练任务,发现 GPU 利用率死活上不去,最后定位到是内存带宽成了瓶颈——数据从显存搬到计算单元的路上,堵车了。

这一讲,咱们就深入看看这个「数据搬运」的体系。说白了,就是内存层次结构、芯片间互联,还有分布式存储系统。

一、内存层次结构:HBM、DDR、SRAM

先看一张图,帮你快速建立整体认知。

深度学习训练内存层次结构 寄存器 / SRAM 容量: KB级 | 延迟: ~1ns HBM (高带宽显存) 容量: 40-80GB | 带宽: 2-3.35TB/s DDR (系统内存) 容量: 64-512GB | 带宽: 50-100GB/s 速度递减 容量递增

这张图很直观:越往上,速度越快,但容量越小。越往下,容量越大,但速度越慢。训练大模型时,你得在这两者之间找平衡。

1. SRAM:计算单元的「高速缓存」

SRAM 是离计算单元最近的内存。它有多快?延迟大概 1 纳秒左右。但代价也很明显——贵,而且占芯片面积大。所以 GPU 里的 SRAM 通常只有几十 KB 到几 MB。

我习惯把 SRAM 看作「工作台」。你想想看,你要做一道复杂的菜,工作台上只能放得下几样食材和工具。你得频繁地从冰箱(HBM)里拿东西,放到工作台上处理。

关键点:在 CUDA 编程中,__shared__ 关键字分配的就是 SRAM。合理利用共享内存,能把访存延迟从几百个时钟周期降到个位数。

我的经验:我曾经优化过一个矩阵乘法的 kernel,一开始直接读全局内存,带宽利用率只有 30%。后来改用共享内存做分块(tiling),带宽利用率直接飙到 85% 以上。嗯,这就是 SRAM 的威力。

2. HBM:GPU 的「主存」

HBM(高带宽显存)是现代 GPU 的标配。它通过 3D 堆叠技术,把多个 DRAM 芯片叠在一起,中间用硅通孔(TSV)连接。这样做的好处是——带宽极高。

拿 NVIDIA A100 来说,HBM2e 的带宽是 2 TB/s。H100 的 HBM3 更是达到了 3.35 TB/s。这个数字什么概念?相当于每秒能读取 1000 部高清电影的数据量。

GPU 型号 HBM 版本 显存容量 带宽
V100 HBM2 16/32 GB 900 GB/s
A100 HBM2e 40/80 GB 2.0 TB/s
H100 HBM3 80 GB 3.35 TB/s

避坑指南:我曾经遇到过一个情况,模型参数量刚好超过显存容量,结果触发了 CPU 内存回退(host memory fallback),训练速度直接掉了 10 倍。所以,选 GPU 时一定要算好显存余量,留出至少 20% 的 buffer。

3. DDR:系统内存的「大后方」

DDR 是 CPU 的系统内存。它的带宽比 HBM 低一个数量级,但容量可以做得很大。在分布式训练中,DDR 主要用来做数据预处理和缓存。

举个例子,你用 PyTorch 的 DataLoader 加载数据时,数据先读到 DDR 里,然后通过 PCIe 传到 GPU 的 HBM 中。如果 DDR 带宽不够,数据加载就会成为瓶颈。

# 一个常见的性能瓶颈场景
# 假设你的 GPU 利用率只有 60%
# 检查一下是不是数据加载的问题

import torch
from torch.utils.data import DataLoader

# 设置 num_workers 来利用多核 CPU 预加载数据
# 我一般设置为 CPU 核心数的一半
dataloader = DataLoader(dataset, batch_size=64, 
                        num_workers=8,  # 关键参数
                        pin_memory=True)  # 锁页内存,加速 CPU->GPU 传输

二、NVLink 与 PCIe:芯片间的「高速公路」

单卡训练已经不够用了,多卡互联是常态。这时候,卡与卡之间的互联带宽就成了关键。

1. PCIe:通用但慢速的互联

PCIe 是 CPU 和 GPU 之间的标准接口。目前主流的是 PCIe 4.0 x16,单向带宽约 16 GB/s。PCIe 5.0 翻倍到 32 GB/s。

但说实话,这个带宽在多卡训练中是不够的。你想想看,一个 7B 参数的模型,光同步梯度就要传 14 GB 的数据(每个参数 4 字节)。用 PCIe 4.0 传一次就要将近 1 秒。

关键点:PCIe 是树形拓扑,所有设备共享带宽。如果你插了 8 张卡,实际每张卡能用的带宽远低于理论值。

2. NVLink:专为 GPU 设计的「高速通道」

NVLink 是 NVIDIA 的私有互联技术。它采用网状拓扑,每张卡直接和其他卡相连,不经过 CPU。延迟低,带宽高。

A100 的 NVLink 3.0 每通道 50 GB/s,总共 12 条通道,双向带宽 600 GB/s。H100 的 NVLink 4.0 更是达到了 900 GB/s。

互联技术 带宽(单向) 拓扑 适用场景
PCIe 4.0 x16 16 GB/s 树形 CPU-GPU 通信
PCIe 5.0 x16 32 GB/s 树形 CPU-GPU 通信
NVLink 3.0 (A100) 300 GB/s 网状 GPU-GPU 通信
NVLink 4.0 (H100) 450 GB/s 网状 GPU-GPU 通信

我的经验:在 8 卡 A100 上做数据并行训练时,我习惯用 torch.distributed 的 NCCL 后端。它会自动利用 NVLink 做梯度 all-reduce,比用 Gloo 后端(走 PCIe)快 5-8 倍。

三、分布式存储系统:数据怎么喂给集群

单机多卡还不够,现在动不动就是几十上百台机器。这时候,数据怎么高效地喂给所有 GPU,就成了大问题。

1. 分布式文件系统

常见的方案有 NFS、Lustre、GPFS 等。它们把多台机器的硬盘组合成一个统一的存储池,所有训练节点都能访问。

但这里有个坑:网络延迟。如果存储节点和计算节点之间走的是千兆以太网,那读取速度可能只有 100 MB/s 左右。对于大模型训练来说,这太慢了。

避坑指南:我曾经在一个 64 卡集群上训练,发现每次 epoch 开始都要等 10 分钟加载数据。后来排查发现,是 NFS 服务器的带宽被其他任务占满了。解决方案是改用本地 SSD 做缓存,或者用高速网络(如 InfiniBand)连接存储。

2. 数据预处理流水线

为了不让数据加载成为瓶颈,我一般会设计一个多级流水线:

  1. 原始数据:存在分布式文件系统上(如 HDFS 或 S3)
  2. 预处理:用 CPU 集群把数据预处理成二进制格式(如 TFRecord 或 MMap)
  3. 缓存:在每台机器的本地 SSD 上缓存一份
  4. 加载:训练时从本地 SSD 读取,通过 DataLoader 喂给 GPU
# 一个典型的数据流水线配置
# 假设你有 8 台机器,每台 8 张 GPU

# 1. 数据预处理(在 CPU 节点上运行)
# python preprocess.py --input /shared/data/raw --output /shared/data/processed

# 2. 训练时,每个节点先缓存数据到本地
# rsync -avz /shared/data/processed/ /local/ssd/data/

# 3. 训练脚本中设置 DataLoader
dataloader = DataLoader(
    dataset,
    batch_size=32,
    num_workers=4,  # 每个 GPU 对应 4 个 worker
    prefetch_factor=2,  # 预取 2 个 batch
    pin_memory=True
)

3. 高性能网络:InfiniBand

当集群规模超过 32 张 GPU 时,我强烈建议上 InfiniBand。它的带宽能达到 200-400 Gbps,延迟只有微秒级。相比之下,千兆以太网只有 1 Gbps,延迟是毫秒级。

说白了,InfiniBand 就是为高性能计算设计的网络。它支持 RDMA(远程直接内存访问),数据可以直接从一个 GPU 的显存传到另一个 GPU 的显存,不经过 CPU 和系统内存。

关键点:在分布式训练中,通信开销占总时间的比例会随着 GPU 数量增加而上升。当 GPU 数量超过 64 张时,通信可能占到 30-50% 的时间。这时候,InfiniBand 和 NVLink 的配合就至关重要了。

好了,这一讲的内容就到这里。内存层次结构、芯片互联、分布式存储,这三者构成了高性能训练的「数据高速公路」。下一讲,咱们聊聊怎么在这条高速公路上跑得更快——也就是分布式训练策略。


想要更多资料,联系可可