一、显存碎片问题概述

什么是显存碎片?

显存碎片,说白了就是显存空间被切割成一块块不连续的小区域。

我打个比方你就明白了。想象你有一个大仓库,里面堆满了各种大小的箱子。你不断搬进新箱子,又搬走旧箱子。时间一长,仓库里就出现了很多小空隙——每个空隙都不大,但加起来浪费了不少空间。

显存碎片也是这个道理。GPU显存被频繁分配和释放后,空闲区域变得支离破碎。这时候,即使总空闲显存足够大,也可能装不下一个连续的大张量。

核心定义:显存碎片是指显存中可用但非连续的空闲内存块,导致无法满足大块连续内存分配请求的现象。

碎片产生的根本原因

为什么会这样?我总结下来,主要有三个原因。

1. 分配与释放模式不匹配

训练过程中,张量的生命周期各不相同。有些张量存活时间长(比如模型参数),有些则很短(比如中间激活值)。

举个例子:

# 典型的训练循环中,张量生命周期差异巨大
model_params = torch.nn.Linear(1024, 1024)  # 长期存活
for batch in dataloader:
    hidden = model(batch)  # 短期存活
    loss = loss_fn(hidden, target)  # 更短生命周期
    loss.backward()
    optimizer.step()

这种长短不一的分配模式,就像在仓库里频繁搬进搬出不同大小的箱子。小箱子搬走后留下的空隙,往往不足以容纳下一个大箱子。

2. 内存分配器的默认行为

PyTorch的CUDA分配器默认使用缓存机制。它不会把释放的内存立刻还给操作系统,而是留着复用。

我遇到过这样的情况:明明显存使用量看起来不高,但一分配大张量就报OOM。原因就是缓存池里的碎片太多了。

小技巧:可以用 torch.cuda.memory_summary() 查看当前显存碎片情况。我习惯在训练脚本里定期打印这个信息。

3. 张量大小不统一

不同batch size、不同序列长度,都会导致张量尺寸变化。每次分配的大小都不一样,碎片自然就多了。

你想想看,如果每次分配的都是固定大小的块,那释放后就能完美复用。但现实是,训练过程中张量尺寸千变万化。

场景 张量大小变化 碎片风险
固定batch size 基本不变 低
动态batch size 频繁变化 中
变长序列 剧烈变化 高

碎片对训练推理的影响

碎片带来的问题,我亲身经历过不少。这里挑几个典型的说说。

1. 显存利用率下降

最直接的影响就是显存利用率上不去。明明有80GB显存,实际能用的连续空间可能只有60GB。

我曾经在一个大模型训练任务中,看着显存占用显示75GB,但分配一个8GB的张量却报OOM。查了半天,发现就是碎片搞的鬼。

2. 训练吞吐量降低

碎片多了,内存分配器需要花更多时间查找合适的空闲块。这个查找过程不是O(1)的,碎片越多,查找越慢。

注意:碎片严重时,分配延迟可能从微秒级飙升到毫秒级。对于频繁分配张量的训练场景,这个开销不可忽视。

3. 推理时延不稳定

推理服务对延迟敏感。碎片导致的分配延迟波动,会让推理时延出现毛刺。

我记得有一次线上推理服务,P99延迟突然从10ms飙到50ms。排查下来,就是显存碎片导致某些请求分配内存时卡住了。

4. 模型规模受限

最头疼的是,碎片会限制你能训练的模型大小。即使总显存够大,但连续空间不够,大模型就放不进去。

说白了,碎片就是显存里的「内耗」。它不直接消耗显存,但让你用不了全部显存。

一句话总结:显存碎片是GPU训练和推理中「看不见的敌人」,它悄悄降低你的效率,限制你的模型规模,却不容易被直接发现。

显存碎片问题知识体系 显存碎片 分配释放模式不匹配 内存分配器缓存机制 张量大小不统一 显存利用率下降 训练吞吐量降低 推理时延不稳定 模型规模受限 核心问题:总显存够,但连续空间不够

好了,这一章我们聊了显存碎片是什么、怎么来的、有什么影响。下一章我会讲讲怎么检测和定位碎片问题,到时候给你看几个我实际用过的工具和脚本。

可可精品资料库