get_max_memory() adds a separate cpu entry on integrated CUDA GPUs, double-counting shared RAM (GB10 / DGX Spark)

该问题出现在 NVIDIA GB10 / DGX Spark 这类集成 CUDA 显存的设备上,`get_max_memory()` 会同时输出设备显存条目和独立的 `cpu` 内存条目,导致同一块物理内存被重复计算。优先检查 `get_max_memory()` 返回的字典中是否存在 `cpu`

快速结论:该问题出现在 NVIDIA GB10 / DGX Spark 这类集成 CUDA 显存的设备上,`get_max_memory()` 会同时输出设备显存条目和独立的 `cpu` 内存条目,导致同一块物理内存被重复计算。优先检查 `get_max_memory()` 返回的字典中是否存在 `cpu` 条目,以及设备属性 `torch.cuda.get_device_properties(0).is_integrated` 是否为 1。

适用环境:Hugging Face Accelerate 1.12.0(issue 报告版本);NVIDIA GB10 / DGX Spark(集成内存的 CUDA 设备);PyTorch 2.10.0a0(已确认 `is_integrated` 属性存在);Linux(`/proc/meminfo` 可查)。issue 中提及 GH200、GB200、Jetson Thor/Orin 可能受影响,但并未逐一验证。

最快修复方案:暂无确认的一步修复方案。截至 issue 关闭,修复 PR #4187 已被提出并等待在真实 GB10 硬件上验证,但 issue 中没有明确记录该 PR 是否已合并及验证结果。

注意事项:该问题会导致依赖 `get_max_memory()` 的 `infer_auto_device_map` 和 `device_map=”auto”` 将同一块物理内存误认为两个独立内存池,模型可能被错误规划为跨“两个”内存分片,最终在加载时报错。issue 作者明确表示,未能复现 4-bit 加载失败与重复计算之间的因果关系,因此不应将全部故障归咎于此问题。

问题场景

用户在使用 Hugging Face Accelerate 的 get_max_memory() 函数,或间接调用它的 infer_auto_device_map / device_map="auto" 功能(例如通过 transformers 加载大模型)时,在拥有集成 CUDA 显存(Unified Memory)的设备(如 NVIDIA GB10 / DGX Spark)上触发。这类设备没有独立的显存,GPU 与 CPU 共享同一物理 RAM,但函数仍会为它们分别生成内存预算条目,导致总内存被虚高上报。

报错原文

get_max_memory() adds a separate cpu entry on integrated CUDA GPUs, double-counting shared RAM (GB10 / DGX Spark)

# 现象示例
>>> import torch
>>> from accelerate.utils import get_max_memory
>>> torch.cuda.get_device_properties(0).is_integrated
1
>>> get_max_memory()
{0: 73446760448, 'cpu': 87132405760}

# 但实际物理内存为
$ grep MemTotal /proc/meminfo
MemTotal:       127535336 kB        # 127.5 GB

# 衍生报错(用户调试时遇到,但未确认与本问题有直接因果)
"Some modules are dispatched on the CPU or the disk"

原因分析

可能原因:源代码 src/accelerate/utils/modeling.py(约 822 行)在生成最大内存字典时,只对 Apple MPS(Metal Performance Shaders)设备做了共享内存的特殊处理,即只输出一个 mps 条目而不输出独立的 cpu 条目。其余的 CUDA 设备都统一走 else 分支,将 psutil.virtual_memory().available 全部计入 cpu 条目。然而,对集成 CUDA 设备(如 GB10)来说,其“显存”和“系统内存”本质上都是同一块物理 RAM,于是这两个条目实际上指向同一块内存,最终在返回的内存预算字典中被重复计算。

环境排查

  • 确认设备是否为集成内存架构:torch.cuda.get_device_properties(0).is_integrated 返回 1 或 True
  • 核对物理内存总量:检查 /proc/meminfo 中的 MemTotal,将其与 get_max_memory() 返回的各条目数值相加的结果对比。
  • 检查 Accelerate 版本(issue 中报告为 1.12.0)以及 PyTorch 版本。注意 setup.py 允许 torch>=2.0.0,但 is_integrated 属性仅在报告者测试的 torch 2.10 上确认存在,旧版本可能没有该属性,导致无法正确判断。
  • 排查是否有下游组件(如 infer_auto_device_map)依赖返回字典中一定存在 cpu 键。由于 MPS 分支一向不返回 cpu 键且能正常工作,理论上集成 CUDA 设备也适用同样逻辑,但如果发现假设 cpu 键存在的代码,需单独确认(该问题在 Apple Silicon 上同样存在)。

解决步骤

可优先尝试(等待修复合并):在官方修复发布前,可以暂时绕过 get_max_memory(),手动指定 device_mapmax_memory 参数,避免对同一物理内存的重复计算。例如,issue 作者使用 device_map={"": 0} 解决其遇到的加载问题。

  1. 检查当前 Accelerate 版本并备份当前工作环境。参考 PR #4187 的描述,该 PR 针对此问题提出了修复方向(方案 A:扩展 MPS 的共享内存处理逻辑到集成 CUDA 设备;或方案 B:修复 infer_auto_device_map 的消费者逻辑)。确认该 PR 是否已包含在更新版本中,可优先尝试升级到包含修复的版本。
  2. 若无法升级,可手动验证问题:在 Python 中调用 torch.cuda.get_device_properties(0).is_integrated,若返回 1,再调用 get_max_memory()。对比返回字典中 'cpu' 键的值与实际 /proc/meminfo。如果两者明显不成比例(例如返回的 'cpu' 值接近全部物理内存,而设备条目又额外占用大量内存),则确认重复计算。(此项为排查确认步骤,并非修复方案)
  3. 在 Accelerate GitHub 仓库上跟踪 issue #4183 和 PR #4187 的状态,等待官方修复版本发布。Issue 报告者表示,在修复合并前可在其 GB10 真机上进行验证,但目前尚无最终合并和验证的公开结论。

验证方法

在集成 CUDA 设备上运行修复后的代码,调用 get_max_memory(),确认其返回值只包含一个指向物理内存的条目(类似于 MPS 分支),不再同时包含 {0: ...}(或对应设备索引)和 'cpu': ... 两个指向同一块内存的键。更直接的验证是:将返回字典中所有条目的数值相加,总和不应超过 /proc/meminfo 中的 MemTotal(即 127.5 GB)。由于 issue 中没有公开的已验证修复方案,此为基于问题描述推导出的验证目标。

参考来源

huggingface/accelerate #4183

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 22338

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注