1、显存溢出初体验:什么是OOM?
1.1 先聊聊显存和内存的区别
很多刚入门GPU编程的同学,会把显存和内存搞混。我刚开始学CUDA时也犯过这个错。
简单来说:
- 内存(RAM):CPU用的,一般几十个GB。你写Python代码时创建的变量、列表,都放在这里。
- 显存(VRAM):GPU用的,一般几个GB到几十个GB。你训练模型时加载的数据、参数,都放在这里。
两者物理上是分开的。数据要从内存搬到显存,GPU才能算。这个搬运过程,就是cudaMemcpy干的事。
核心区别一句话:内存是CPU的仓库,显存是GPU的工地。工地太小,材料堆不下,就OOM了。
1.2 什么是OOM错误?
OOM全称Out Of Memory,就是显存不够用了。
你想想看,GPU的显存是有限的。当你申请一块显存,比如cudaMalloc一个10GB的数组,但你的GPU只有8GB显存——那CUDA就会直接报错。
常见的报错信息长这样:
RuntimeError: CUDA out of memory. Tried to allocate 10.00 GiB
GPU 0 has a total capacity of 8.00 GiB
嗯,看到这个红色报错,基本就是OOM了。
注意:OOM不一定是显存真的用完了。有时候是显存碎片化严重,虽然总量够,但连续的大块空间不够。这个后面会细讲。
1.3 一个简单的OOM触发案例
我们来写个代码,亲手触发一次OOM。这样你以后看到报错,心里就有数了。
#include <stdio.h>
#include <cuda_runtime.h>
int main() {
// 假设你的GPU只有4GB显存
size_t size = 6UL * 1024 * 1024 * 1024; // 6GB
float *d_ptr;
cudaError_t err = cudaMalloc(&d_ptr, size);
if (err != cudaSuccess) {
printf("OOM错误:%s\n", cudaGetErrorString(err));
} else {
printf("显存分配成功!\n");
cudaFree(d_ptr);
}
return 0;
}
这段代码试图分配6GB显存。如果你的GPU只有4GB,那cudaMalloc就会返回cudaErrorMemoryAllocation。
我在项目中遇到过类似情况:有一次训练一个大模型,batch size设得太大,结果一跑就OOM。当时我还纳闷,明明显存还有几个GB啊?后来才发现是中间变量太多,把显存撑爆了。
避坑指南:我曾经在调试一个推理服务时,发现每次请求都会OOM。查了半天,原来是代码里忘了cudaFree,显存泄漏了。所以记住:每次cudaMalloc,都要配一个cudaFree。
1.4 显存分配的核心流程
为了让你更直观地理解OOM是怎么发生的,我画了一张图:
1.5 如何快速判断是不是OOM?
当你的程序崩溃时,先看报错信息。如果是CUDA out of memory,那就是OOM。但有时候报错不直接,比如程序卡死、显存突然归零,也可能是OOM导致的。
我常用的排查命令:
# 查看显存使用情况
nvidia-smi
# 或者更详细的
nvidia-smi --query-gpu=memory.total,memory.used,memory.free --format=csv
看到显存使用率接近100%,基本就是OOM了。
一个小技巧:在代码里加cudaDeviceSynchronize(),可以帮你定位到底是哪一步OOM的。我调试时经常这么干。
1.6 本章小结
说白了,OOM就是显存不够用。显存和内存是两码事,别搞混。触发OOM很简单,申请超过显存总量的空间就行。但实际项目中,OOM往往是因为代码写得不够精细——比如忘了释放显存、batch size设得太大、中间变量太多。
嗯,这一章先让你对OOM有个直观感受。后面我们会深入讲怎么避免、怎么优化。
个人习惯:我每次写CUDA代码,都会在关键位置打印显存使用情况。这样一旦OOM,马上就能知道是哪一步出了问题。推荐你也试试。
想要更多资料,联系可可