1. 显存碎片问题概述

大家好,我是你们的老朋友。今天咱们来聊聊显存碎片——这个在高并发场景下让人头疼的问题。

先问个问题:你遇到过模型推理时突然OOM,但nvidia-smi一看显存还剩不少的情况吗?

嗯,我遇到过。而且不止一次。

有一次线上服务半夜告警,说推理请求大面积超时。我登录上去一看,显存占用率才70%,但新请求就是分配不出内存。当时我就意识到——显存碎片来了。

什么是显存碎片

显存碎片,说白了就是显存空间被切割成许多小块。这些小块单独看都不大,但加起来总量不少。问题是,新来的大块请求找不到连续的空间。

就像你有一块完整的空地,被人这里挖个坑、那里堆个包。最后空地还剩不少,但想停一辆大卡车?没门。

在GPU里,显存分配以页(page)为单位。CUDA默认用4KB的页。频繁分配和释放,就会产生大量不连续的空闲页。

核心定义:显存碎片是指显存中可用但非连续的空闲内存块。这些碎片无法满足大块连续内存的分配请求,导致实际可用显存远小于理论值。

碎片产生的根本原因

为什么会碎?我总结了三层原因。

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

你分配一块128MB的显存,用完后释放。但下一个请求要256MB。128MB的空闲块就卡在那里,成了碎片。

我做过一个实验:连续分配不同大小的tensor,然后随机释放。仅仅1000次操作后,显存碎片率就超过了30%。

2. CUDA内存管理机制

CUDA驱动本身会缓存已释放的显存,避免频繁与内核交互。这个缓存策略,说白了就是「先留着,万一有人用呢」。

但问题来了:缓存块的大小和位置是固定的。新请求的大小如果和缓存块不匹配,就得重新分配。旧的缓存块就成了碎片。

// 一个典型的碎片产生场景
cudaMalloc(&ptr1, 1024 * 1024);      // 分配1MB
cudaMalloc(&ptr2, 512 * 1024);       // 分配512KB
cudaFree(ptr1);                       // 释放1MB,产生空洞
cudaMalloc(&ptr3, 768 * 1024);       // 需要768KB,但空洞只有1MB
// 1MB的空洞被分割:768KB被使用,剩下256KB碎片

3. 内存对齐要求

GPU对内存对齐有严格要求。通常是256字节或512字节对齐。这意味着你申请100字节,实际占用的是256字节。多出来的156字节,别人用不了。

这些小缝隙日积月累,就成了大问题。

避坑指南:我曾经在项目里用cudaMalloc频繁分配小tensor(几十KB级别),结果显存碎片率一周内飙到45%。后来改用内存池,问题才解决。

为什么高并发场景下更严重

这个问题,我当年在优化一个推荐系统推理服务时深有体会。

单线程场景下,分配释放是有序的。就像排队买饭,一个一个来,窗口始终整洁。

高并发场景呢?多个请求同时进来,同时分配,同时释放。顺序完全乱了。

我画了一张图,帮你理解这个过程:

高并发 vs 单线程:显存碎片对比 单线程(有序分配) 请求A: 128MB 请求B: 64MB 请求C: 256MB 释放A: 128MB空闲 释放B: 64MB空闲 释放C: 256MB空闲 合并后:448MB连续空闲 高并发(乱序分配) 请求A: 128MB 请求B: 64MB 请求C: 256MB 请求D: 32MB 释放A: 空闲 B在用 释放C: 空闲 D在用 碎片化:128MB + 256MB 不连续 高并发场景碎片加剧的三大原因 1. 分配释放顺序随机,相邻块无法合并 2. 多线程竞争导致内存分配器频繁分裂大块 3. 请求大小分布更广,对齐浪费更严重

你看,单线程下释放的块是相邻的,可以合并成一个大块。高并发下,释放的块被其他请求「插队」占用,碎片就散落在各处。

具体来说,高并发场景有三个「杀手」:

  1. 分配释放顺序随机化:多个请求的生命周期不同。有的请求快,有的慢。释放顺序和分配顺序完全错位。
  2. 内存分配器压力增大:CUDA的内存分配器需要处理更多并发请求。它不得不频繁分裂大块来满足小请求,导致碎片增多。
  3. 请求大小分布更广:高并发下,不同请求处理的数据量差异大。从几KB到几百MB都有。这种大小混杂的情况,最容易产生碎片。

注意:我见过一个极端案例——某公司的推理服务,8张A100显卡,每张80GB显存。高并发下实际可用显存只有55GB左右。碎片率超过30%!

这意味着你花了80GB的钱,只买到55GB的性能。亏不亏?

碎片带来的实际影响

碎片不只是浪费显存。它还会带来性能问题。

影响维度 具体表现 严重程度
显存利用率 实际可用显存减少20%-40% 高
分配延迟 cudaMalloc耗时增加3-10倍 中
服务稳定性 突发OOM导致请求失败 高
吞吐量 因内存不足被迫降低并发数 中

说白了,显存碎片就是「隐形杀手」。它不会直接报错,但会慢慢蚕食你的系统性能。

我的经验:排查显存碎片问题,我一般先看两个指标:

  • nvidia-smi显示的已用显存 vs 实际分配总量
  • cudaMalloc的调用耗时是否异常增加

如果前者差距大,后者耗时波动明显,十有八九是碎片问题。

好了,这一章我们搞清楚了什么是显存碎片、它怎么来的、为什么高并发下更严重。下一章我会讲具体的检测方法和量化指标。到时候咱们用代码说话。

想要更多资料,联系可可