显存基础:从硬件到实战
大家好,我是你们的老朋友。今天咱们聊聊显存。
说实话,我刚开始做深度学习那会儿,对显存的理解就是「显存越大越好」。后来踩了不少坑,才慢慢摸清楚这里面的门道。你想想看,一个模型训练到一半,突然爆显存,那种感觉……嗯,懂的都懂。
所以这一章,咱们把显存的基础打扎实。别急,我会用我自己的经验,帮你避开那些我当年踩过的坑。
什么是显存?
显存,全称是「显示内存」。说白了,就是显卡上专门用来存数据的内存。
在深度学习里,显存里存的是啥?
- 模型参数(权重、偏置)
- 中间激活值(每一层的输出)
- 梯度信息
- 优化器状态(比如 Adam 的动量)
- 输入数据(图片、文本等)
我习惯把显存想象成「GPU 的工作台」。工作台越大,你能同时处理的东西就越多。但工作台再大,也有放不下的时候——这时候就得想办法优化了。
核心要点:显存是 GPU 的「临时仓库」。模型训练时,所有需要快速访问的数据都得放在这里。
显存 vs 内存:到底有啥区别?
很多人搞混这两个概念。我刚开始也犯过这个错。
简单来说:
- 内存(RAM):CPU 用的,存操作系统、应用程序、数据。容量大(16GB、32GB 甚至更大),但速度相对慢。
- 显存(VRAM):GPU 用的,专门给显卡处理图形和计算任务。容量小(8GB、16GB、24GB 常见),但速度极快。
为什么会这样?因为 GPU 需要频繁读写数据,显存必须用高带宽的 HBM 或 GDDR 技术。内存用的是 DDR,带宽差了一个数量级。
| 特性 | 内存(RAM) | 显存(VRAM) |
|---|---|---|
| 用途 | CPU 数据存储 | GPU 数据存储 |
| 典型容量 | 16-64 GB | 8-48 GB |
| 带宽 | ~50 GB/s | ~900 GB/s(HBM2e) |
| 延迟 | ~100 ns | ~200-400 ns |
| 物理位置 | 主板上 | 显卡 PCB 上 |
避坑指南:我曾经以为把数据放在内存里,GPU 也能直接访问。结果发现每次 GPU 要数据,都得先通过 PCIe 总线从内存拷贝到显存。这个拷贝过程慢得让人抓狂。所以记住:GPU 只认显存,不认内存。
GPU 架构简介:显存在哪?
咱们拿 NVIDIA 的 GPU 举个例子。一张显卡的核心架构大致是这样的:
这张图是我自己画的,方便你理解。GPU 核心(SM)直接连接 L1 缓存,然后通过 L2 缓存访问显存。显存控制器负责管理数据进出。
我个人的经验是:显存带宽比容量更重要。为什么?因为模型训练时,数据搬运的速度往往成为瓶颈。你容量再大,带宽不够,GPU 核心就得干等着。
小技巧:选显卡时,别只看显存大小。看看带宽参数。比如 A100 的 HBM2e 带宽是 1.6 TB/s,而 RTX 3090 的 GDDR6X 是 936 GB/s。差了一倍。
显存带宽与容量:怎么选?
这个问题我经常被问到。我的回答是:看你的任务。
- 大模型训练(LLM、ViT):容量优先。模型参数动辄几十 GB,显存不够根本跑不起来。
- 高分辨率图像处理:带宽优先。每张图数据量大,需要快速搬运。
- 实时推理:带宽优先。延迟要求高,数据必须快进快出。
举个例子。我去年训练一个 7B 参数的 LLaMA 模型,单张 A100 80GB 勉强能塞下。但 batch size 只能设 1。如果换成 4 张 RTX 3090 24GB,虽然总容量 96GB,但跨卡通信的开销让训练速度慢了一半。
所以,容量决定你能不能跑,带宽决定你跑得快不快。
| 显卡型号 | 显存容量 | 显存带宽 | 适合场景 |
|---|---|---|---|
| RTX 3060 | 12 GB | 360 GB/s | 小模型、入门 |
| RTX 3090 | 24 GB | 936 GB/s | 中等模型、炼丹 |
| A100 80GB | 80 GB | 1.6 TB/s | 大模型、训练 |
| H100 | 80 GB | 2.0 TB/s | 超大模型、训练 |
注意:别被「显存共享」这种技术忽悠了。有些显卡支持从系统内存借空间,但速度慢得离谱。我曾经试过,训练速度直接掉到原来的 1/10。除非万不得已,别用。
实战:如何查看显存使用情况?
说了这么多理论,来点实际的。怎么知道自己显存用了多少?
我常用的命令:
# 查看 GPU 状态
nvidia-smi
# 实时监控
watch -n 1 nvidia-smi
# Python 里查看
import torch
print(torch.cuda.memory_allocated()) # 已分配显存
print(torch.cuda.memory_reserved()) # 预留显存
print(torch.cuda.max_memory_allocated()) # 最大分配显存
我个人习惯在训练脚本里加个显存监控,每 100 步打印一次。这样能及时发现显存泄漏或者异常增长。
经验之谈:显存泄漏是常见问题。如果你发现训练过程中显存持续增长,不释放,那八成是代码里有变量没释放,或者 DataLoader 的 num_workers 设置有问题。
好了,这一章就到这里。显存的基础概念、架构、带宽和容量,咱们都聊透了。下一章开始,我会带你看看怎么用代码优化显存——那些我踩过的坑,你就不用再踩了。
想要更多资料,联系可可