一、显存碎片化概述

什么是显存碎片化?

显存碎片化,说白了就是你的 GPU 显存明明还有不少空闲,但就是分配不出一块连续的大空间来加载模型或 batch。

我刚开始做深度学习训练时,遇到过这么个怪事:一张 24G 的显卡,nvidia-smi 一看还剩 12G,可一跑训练就报「CUDA out of memory」。当时我整个人都懵了——这 12G 去哪了?

后来才明白,这 12G 被切成了几十块小碎片,每块几百 MB 到 1GB 不等。你要申请一块 4G 的连续空间?对不起,没有一块碎片够大。这就是显存碎片化。

核心定义:显存碎片化是指显存中可用空闲空间被分割成多个不连续的小块,导致无法满足大块连续内存分配请求的现象。

碎片化的分类

碎片化其实分两种,我习惯把它们叫做「内伤」和「外伤」。

1. 内部碎片

内部碎片,就是分配给你的空间你用不完。举个例子:你申请了 512MB,CUDA 底层给你分配了一个 1MB 的 block,但实际只用了 600KB。那剩下的 424KB 就浪费了,别人也用不了。

为什么会这样?因为 CUDA 的内存分配是按固定粒度来的。我记得 CUDA 的分配粒度是 512 字节?不对,实际上更复杂,跟页大小和分配器实现有关。但道理是一样的——你申请的大小和实际分配的大小之间总有缝隙。

我的经验:内部碎片在 PyTorch 的缓存分配器中尤其明显。PyTorch 会预分配大块显存,然后自己管理。如果你频繁创建和销毁小张量,内部碎片会迅速累积。

2. 外部碎片

外部碎片才是真正的「杀手」。它指的是显存中散布着许多空闲的小块,但每块都不够大。

你想想看,训练过程中我们会不断创建中间张量:前向传播的激活值、反向传播的梯度、优化器的状态……这些张量生命周期不同,有的几毫秒就释放了,有的要保留到整个 step 结束。

释放和分配反复进行,显存就像一块被反复切割的蛋糕,最后全是碎渣。

碎片类型 产生原因 典型场景 影响程度
内部碎片 分配粒度不匹配 小张量频繁创建 中等
外部碎片 分配/释放顺序混乱 动态计算图、变长序列 严重

碎片化对深度学习的影响

影响有多大?我直接说结论:轻则 OOM,重则训练中断,甚至模型根本跑不起来。

训练阶段的影响

  • batch size 受限:明明显存总量够,但就是放不下更大的 batch。我有个项目,原本可以跑 64 的 batch,碎片化严重后只能跑 32,训练速度直接腰斩。
  • 训练不稳定:偶尔报 OOM,重启后又好了。这种「薛定谔的显存」最让人抓狂。
  • 动态图场景更惨:PyTorch 的动态图每次 forward 都会创建新张量,碎片化累积速度比 TensorFlow 静态图快得多。

推理阶段的影响

  • 延迟抖动:推理服务中,显存碎片化会导致某些请求分配内存耗时变长,延迟从 5ms 飙到 50ms。
  • 吞吐量下降:为了避开碎片化,你不得不降低并发数。我优化过一个推理服务,解决碎片化后吞吐量提升了 40%。
  • 模型加载失败:多模型部署时,第二个模型可能因为碎片化加载不上。明明总显存够,就是报错。

避坑指南:我曾经在一个 8 卡 A100 的机器上跑大规模分布式训练,结果第 7 张卡总是 OOM。排查了两天,发现是某张卡上碎片化特别严重。最后用 cuda-memcheck 一查,是某个数据加载线程在偷偷分配小张量没释放。嗯,这种「幽灵分配」最难查。

碎片化的本质

说到底,碎片化是「分配模式」和「释放模式」不匹配造成的。就像你往一个盒子里放不同大小的积木,放进去又拿出来,最后盒子里全是空隙。

CUDA 的内存管理其实很「笨」——它不会自动整理碎片。不像 CPU 的 malloc 有各种优化策略,GPU 端的分配器相对简单。所以,碎片化问题必须由我们开发者自己解决。

一句话总结:显存碎片化不是显存不够,而是显存「不连续」。解决碎片化,就是让显存重新「连起来」。

显存碎片化知识体系 显存碎片化 内部碎片 外部碎片 影响范围 分配粒度不匹配 缓存浪费 分配释放混乱 连续空间不足 训练OOM 推理延迟抖动 核心目标:让显存重新「连起来」

好了,这一章我们理清了碎片化的概念和影响。下一章我会带你看看碎片化到底是怎么产生的——用代码一步步复现出来。到时候你就知道,那些「幽灵 OOM」到底从哪来的了。

可可精品资料库