显存基础:GPU显存架构、带宽与容量、分配与回收机制

大家好,我是你们的老朋友。今天咱们聊聊显存——这个在吞吐压测里最容易出幺蛾子的家伙。

说实话,我见过太多团队,模型跑得挺欢,一上压测就崩。查到最后,十有八九是显存管理出了问题。你想想看,显存就像GPU的“工作台”,东西摆不下,再好的算法也白搭。

一、GPU显存架构:从芯片到板卡

先说说显存长什么样。嗯,这里要注意,显存不是一块独立的内存条,它是焊在显卡PCB板上的颗粒。

目前主流的显存类型是GDDR6和HBM2e。GDDR6常见于消费级显卡,比如RTX 3090、4090。HBM2e则多用于数据中心卡,像A100、H100。

我个人习惯把显存架构分成三层来看:

  • 物理层:显存颗粒本身,决定了总容量和位宽
  • 控制器层:显存控制器,负责调度读写请求
  • 缓存层:L1/L2缓存,减少对显存的直接访问

我在项目中遇到过一件事:某次压测,模型推理速度忽快忽慢。查了半天,发现是L2缓存命中率太低,导致频繁访问显存。说白了,缓存没用好,带宽再大也白费。

核心要点:显存架构的瓶颈往往不在容量,而在带宽和延迟。压测时,先看缓存命中率,再看显存带宽利用率。

GPU显存架构分层示意图 物理层:显存颗粒 GDDR6 / HBM2e 颗粒 | 位宽 384-bit / 1024-bit 控制器层:显存控制器 调度读写请求 | 管理Bank冲突 | 控制刷新周期 缓存层:L1 / L2 Cache 减少显存访问 | 提升数据局部性 | 命中率决定性能 压测时重点关注:缓存命中率 → 显存带宽利用率 → 显存容量

二、显存带宽与容量:两个关键指标

带宽和容量,这两个参数经常被混为一谈。其实它们管的是不同的事。

带宽决定了数据搬运的速度。计算公式很简单:

显存带宽 = 显存频率 × 位宽 × 2(GDDR双倍数据率)

举个例子:
RTX 4090:频率 21 Gbps,位宽 384-bit
带宽 = 21 × 384 / 8 = 1008 GB/s

容量决定了你能装下多大的模型。比如一个70B的LLaMA模型,用FP16加载,光权重就要140GB。你想想看,单卡24GB显存根本装不下。

实战技巧:我建议压测前先算一笔账——模型权重 + 优化器状态 + 中间激活值 + 临时缓冲区。这四个加起来,别超过显存容量的80%。留点余量,不然跑着跑着就OOM了。

显卡型号 显存容量 显存带宽 适用场景
RTX 3090 24 GB 936 GB/s 单卡推理、小模型训练
RTX 4090 24 GB 1008 GB/s 高吞吐推理、中等模型
A100 80G 80 GB 2039 GB/s 大模型训练、多卡并行
H100 80 GB 3352 GB/s 超大模型、专家并行

我曾经踩过一个坑:压测时只看容量,没注意带宽。结果模型是装下了,但推理速度慢得像蜗牛。后来一查,带宽利用率飙到95%,明显是带宽瓶颈。说白了,容量管“能不能装”,带宽管“快不快”。

三、显存分配与回收机制:别让内存泄漏坑了你

这个部分,我觉得是压测里最容易翻车的地方。显存分配和回收,说白了就是操作系统那套“malloc/free”的GPU版。

CUDA里,显存分配主要靠这几个API:

  • cudaMalloc:分配显存,返回设备指针
  • cudaFree:释放显存
  • cudaMallocManaged:统一内存,自动迁移

但问题来了——频繁调用cudaMalloc和cudaFree,性能会非常差。为什么?因为每次分配都要和驱动交互,开销很大。

避坑指南:我曾经在压测脚本里每轮迭代都cudaMalloc一次,结果显存碎片化严重,跑了200轮就OOM了。后来改用显存池(memory pool),一次性分配一大块,自己管理,性能提升30%以上。

显存回收还有个坑:Python的垃圾回收和CUDA显存回收是异步的。你调了del tensor,显存不会立刻释放。得等cuda.synchronize()之后才真正回收。

# 错误示范
import torch
for i in range(1000):
    x = torch.randn(1024, 1024).cuda()
    # 没有显式释放,显存越占越多

# 正确做法
for i in range(1000):
    x = torch.randn(1024, 1024).cuda()
    # 用完就删
    del x
    torch.cuda.empty_cache()  # 建议加上,但别频繁调用

嗯,这里要注意:empty_cache()也不能频繁调,因为它会清空缓存,下次分配又要重新申请,反而降低性能。我个人的习惯是:每100轮调一次,或者显存占用超过80%时触发。

总结一下显存管理的三个原则:

  1. 预分配 > 按需分配(用显存池)
  2. 及时释放 > 依赖GC(手动del)
  3. 监控碎片 > 只看总量(用nvidia-smi看碎片率)

好了,显存基础就聊到这儿。记住,压测不是跑起来就行,得把显存吃透。下节课咱们聊聊怎么用工具监控显存,到时候我会分享几个我压箱底的脚本。

课后小作业:打开nvidia-smi,看看你的显卡显存带宽和容量。再跑一个模型,观察显存分配和释放的过程。试试看,能不能找到碎片化的迹象?

可可精品资料库