loading

Loading

探索与发现

首页 AI 工程化

GPU显存碎片率:那个不爆显存也会挂的指标

分类:AI 工程化
字数: (2405)
阅读: (7)
0
摘要:显存没用满却 OOM,多半不是容量问题。从单卡分配器的碎片率计算和 memory snapshot 定位,到集群调度层的 bin-packing,记录其中的实践与取舍边界。

推理服务里有一种故障很难排查:显存没用满,nvidia-smi 显示还剩几个 GB,但新请求一进来就 OOM。日志里看不到显存泄漏,进程也没变胖。这类问题大概率不是容量不够,是碎片。

显存和磁盘不一样。磁盘上有个 100MB 的空洞,你还能往里写 50MB 的文件;显存分配器在面对一个连续的大块请求时,如果空闲页分散在十几个不连续的位置,这块内存就是申请不到。所以只看「已用/总量」这个指标做容量规划,在动态 shape、长短请求混跑的推理场景里是会出事的。

碎片率怎么算,先别急着上工具

碎片率没有统一定义,常见的口径是「空闲显存中最大的连续块 / 空闲显存总量」,越接近 1 越健康。这个值比绝对空闲量有用得多:空闲 60% 但最大连续块只有 2GB,和空闲 15% 但最大连续块有 8GB,后者能接的请求反而更多。

腾讯云开发者社区一篇解析 vLLM 碎片管理的文章提到,vLLM 集成了 NVIDIA cuMem 相关的工具来获取底层显存使用细节,并定期计算碎片率、做可视化、设阈值告警。这个思路对做工程的人是有参考价值的:把碎片率当成一个常规指标导出,而不是每次出事再人工去看 snapshot。

不过要留个心眼。资料里的宣传话术(“在线整理”“零拷贝整理”“智能决策整理时机”)我建议对照实际版本核验。真正做显存整理要迁移数据、会占带宽、会打断正在进行的 kernel 执行,远不像内存页整理那么便宜。我对「在线、增量、对性能影响小」这种三连说法持保守态度,至少得看自己的负载在整理窗口内的 P99 延迟有没有恶化。

用 snapshot 定位,别靠猜

PyTorch 的 memory snapshot 是最实在的第一步。torch.cuda.memory._record_memory_history() 打开记录,跑一段真实流量后 dump 出来,能在可视化里看到每个块的申请释放来源。max_allocated 和 reserved 之间的差值长期在十几 GB,基本可以确定分配器缓存了太多尺寸不一的小块。

生产治理有一套常规组合拳,值得按顺序试:

  • 把动态 shape 做维度分桶,比如序列长度按 256 的倍数对齐,减少无限多的尺寸档位。
  • 调整 PYTORCH_CUDA_ALLOC_CONF,看过 expandable_segments 是否能减少保留区被切碎的情况。这项在不同 PyTorch 版本上行为有差异,要自己压测。
  • 接住 vLLM 的 gpu_memory_utilization 预算:调高能多吃显存,但给碎片留的余量也跟着变少。遇到诡异 OOM 时,先确认这个值是不是被无脑拉到了 0.95。

治理方案要可观测、可验证、可回滚。具体说,每一项改动前后都拿同一组回放流量跑,对比峰值显存、碎片率、P99 延迟三个数,再决定留不留。

集群层的碎片,是另一个物种

单卡上的碎片是分配器问题,集群上的「GPU 碎片」是调度结果。NVIDIA 的 Ameya Parab 在技术博客里写过 DGX 云上的一个真实案例:Kubernetes 集群里几万个 L40S,Volcano 调度器用默认的 gang scheduling 加随机放置,结果是大量节点只被占用了 3 个 GPU,剩下 1 个空着,需要整节点 4 卡分布式的训练任务排不进去。

他们的做法是把 bin-packing 接进 Volcano,配置里给 binpack.resources.nvidia.com/gpu 加权,让负载优先塞进已部分占用的节点,把整节点腾出来给大活。数据很有分量:改造后集群 GPU 平均占用率约 90%,超过了合同要求的 80%;顺便减少了一类节点命运,即空闲 4 卡却分散在各处的节点,这种节点的数量从 214 降到约 9 个,可直接用的满节点数大涨。

这里要注意 bin-packing 的代价。它追求的是填满,填满意味着小作业和大作业抢“预留位”。如果你的集群要留余地给突发的多卡推理,过度 bin-pack 反而会让大任务等待时间变长。NVIDIA 也没有说这个策略对所有负载都是免费午餐,只说 gang 的“全无”语义应当保留。

另一篇 CSDN 文章提到用 DQN 做显存超卖与重整理,说大模型推理的显存利用率通常只有 35% 到 45%。这个数字我无法核对具体来源口径,所以只把它当作者给出的前提,不当作行业结论。用强化学习做调度决策,听起来诱人,但在调度器上引入模型意味着一个新的失败模式和一套难以回滚的策略。在生产里,rule-based 的 bin-packing 加明确的优先级,赢面往往更大。

落到监控面板上的四件事

真要建监控,我会先上这四个:

  1. 显存碎片率,按实例和模型输出,配合告警阈值,但阈值先用历史 p95 定,别拍脑袋。
  2. allocated/segments 离散度,看分配的块大小分布是否越来越细碎。
  3. OOM 时的 snapshot 自动落盘,别等事后让人去复现。
  4. 集群维度可用整卡节点数,这个数最能解释“模型明明有空卡却调度不进去”的工单。

有一个容易被忽略的事实:碎片是会自我强化的。一次 OOM 触发分配器释放缓存,接着下一个请求又产生新的块尺寸,长稳时间的跑法比短压测更能暴露这些问题。

回头说我遇到的某次事故。10 个 A100 跑了 86300 秒,OOM 的请求里只有不到两成长度超过均值。最后定位到频繁变化的 beam 宽度让每次生成的 KV block 尺寸来回跳。把 decoding 阶段的常用尺寸做了固定余量预留之后,碎片率从 0.31 稳定清零,改造大约半天。这说明碎片排查里最简单的办法是找频繁变化的尺寸维度,哪怕你没有完整工具链,也值得先在前几节的位置加上 instrumentation,不要把它留成盲区。

转载请注明出处: 皮哈皮阿

本文的链接地址: https://blog.phpat.com/ai-engineering/642.html

手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

您可能对以下文章感兴趣
1 / 1
预览图