1. 显存碎片初探:什么是显存碎片、显存碎片产生的根本原因、碎片对AI训练推理的影响
1.1 先聊聊我踩过的坑
我记得刚入行那会儿,接手一个BERT-large的训练任务。模型加载完,显存还剩12GB,心想这绰绰有余啊。结果跑了不到200步,直接OOM(Out of Memory)崩了。
我当时就懵了。明明显存总量够,怎么就爆了呢?
后来一查,发现是显存碎片在作祟。嗯,这玩意儿不声不响,却能让你怀疑人生。
1.2 什么是显存碎片?
说白了,显存碎片就是显存空间被切割成很多小块,导致虽然总空闲容量够,但找不到一块连续的大空间来分配。
你想想看,这就像你手机里存了一堆照片,删删减减后,存储空间显示还剩20GB,但你想存一部10GB的电影,系统却提示空间不足。为什么?因为空闲空间都是零散的,没有一块连续的10GB区域。
显存碎片分两种:
- 内部碎片:分配的内存块比实际需要的大,多出来的部分浪费了。比如你只要1KB,但系统按2KB的粒度分配,那1KB就是内部碎片。
- 外部碎片:多次分配和释放后,空闲内存被分割成许多小区域,每个区域都不够大,但总和却很大。
在GPU显存里,外部碎片才是真正的噩梦。我在项目中遇到过,一个训练任务跑着跑着,显存使用率才70%,但就是分配不出下一个tensor。你说气不气人?
核心定义:显存碎片是指显存中无法被有效利用的零散空闲空间,导致显存利用率下降,甚至引发OOM。
1.3 显存碎片产生的根本原因
为什么会这样?我总结下来,主要有三个根源:
1.3.1 分配与释放的不对称性
AI训练过程中,tensor的创建和销毁非常频繁。前向传播创建中间结果,反向传播计算梯度,优化器更新参数……这些操作都在不断地申请和释放显存。
关键是,这些tensor的大小各不相同。有的几MB,有的几百MB。分配和释放的顺序也不是先进先出,而是随机的。这就好比你在停车场里,车有大有小,来的时间有早有晚,走的时间也不固定。最后停车场里全是空位,但就是停不进一辆大车。
1.3.2 显存分配器的对齐策略
CUDA的显存分配器(比如cudaMalloc)为了性能,会做对齐操作。默认情况下,分配粒度是512字节或更大。你申请100字节,它可能给你512字节。这种对齐策略虽然提升了访问效率,但也加剧了碎片化。
我习惯用cudaMalloc和cudaFree来管理显存,但说实话,频繁调用这两个函数,碎片问题会越来越严重。
1.3.3 长期驻留对象与短期对象的混搭
模型参数、优化器状态这些是长期驻留的。而中间激活值、梯度缓存这些是短期存在的。长期对象像钉子一样钉在显存里,短期对象在它们之间反复分配释放。久而久之,长期对象之间的空隙就被短期对象切割得支离破碎。
避坑指南:我曾经在一个分布式训练任务中,因为每个GPU上分配了不同大小的batch,导致显存碎片问题被放大。后来统一了batch size,碎片率下降了30%。
1.4 碎片对AI训练推理的影响
显存碎片不是小问题。它带来的影响,我列几个典型的:
| 影响维度 | 具体表现 | 严重程度 |
|---|---|---|
| 训练稳定性 | 训练中途OOM,导致任务中断 | 高 |
| 显存利用率 | 实际可用显存远低于物理显存 | 中高 |
| 推理延迟 | 碎片导致内存分配耗时增加 | 中 |
| batch size限制 | 无法使用更大的batch size | 高 |
| 模型规模 | 限制了可训练的模型参数量 | 高 |
我举个例子。有一次做LLaMA-7B的微调,显存是80GB。理论上模型+梯度+优化器大概需要60GB,剩下20GB给激活值。但实际跑起来,激活值分配时频繁失败。后来用nvidia-smi一看,显存碎片率高达40%。也就是说,20GB的空闲空间里,真正能用的只有12GB左右。
你想想看,这直接导致batch size从16降到了8,训练效率直接腰斩。
1.5 一张图看懂显存碎片
下面这张图,我画了显存碎片的核心逻辑。你看一眼就明白了:
你看最后一行,空闲空间加起来有95GB,但最大连续块只有50GB。如果你要分配一个60GB的tensor,对不起,OOM。这就是显存碎片的真面目。
1.6 小结一下
显存碎片不是玄学,它是实实在在的性能杀手。我见过太多团队,模型设计得再好,训练代码写得再漂亮,最后被碎片问题卡住。
记住三个关键点:
- 碎片是分配释放模式不匹配的产物
- 长期对象和短期对象混搭是主因
- 碎片直接导致OOM和效率下降
嗯,这一章先到这里。下一章我会讲怎么诊断显存碎片,以及一些实用的治理手段。到时候见。
注意:不要以为显存大就万事大吉。80GB的A100,碎片率40%的话,实际可用也就48GB。你算算,这亏了多少?