【Bug已解决】`infer_auto_device_map` does not place submodule buffers on `device_map` when submodule is s

发布时间:2026/9/24 14:45:35

【Bug已解决】`infer_auto_device_map` does not place submodule buffers on `device_map` when submodule is s 【Bug已解决】infer_auto_device_mapdoes not place submodule buffers ondevice_mapwhen submodule is split 解决方案一、现象长什么样用accelerate的infer_auto_device_map把一个大模型自动切到多张卡上。多数 submodule如每个decoder.layer.{i}会完整落在一张卡上但当一个 submodule 被拆分到两张卡之间时比如前几层在 GPU0、后几层在 GPU1这个被拆分的 submodule 里的buffer如LayerNorm的running_mean/running_var、loss_fn的缓存、位置编码表没有被写进device_map。于是 forward 时报RuntimeError Expected all tensors to be on the same device but found at least two devices cuda0 and cuda1或者更隐蔽地buffer 留在 CPUforward 时和 GPU 上的参数做运算 - device mismatch最小判据触发某个 submodule 跨设备被拆分split across GPUs 现象该 submodule 的 buffer 不在 device_map 中 后果buffer 滞留在原设备CPU/错误卡forward device mismatch 报错与否forward 时才暴露RuntimeError最迷惑的是大多数 submodule 正常只有被拆分到边界的那一个出问题。你盯着device_map看半天发现它的 parameters 都被分配了唯独 buffer 那几行缺席。二、背景infer_auto_device_map的产出是一个device_map字典module.path: cuda:0之类描述每个 module 落到哪张卡。accelerate在load_checkpoint_and_dispatch/dispatch_model时会按这个 map 用cpu_offload hook 把参数和 buffer 搬到对应设备。实现上算法是按 parameter 占用的显存逐步贪心分配从 GPU0 开始塞塞满就切到 GPU1。一个 submodule 若在边界被切断它的参数被分到两张卡。但 buffer 的处理与 parameter 是两套逻辑——buffer 的归属只在该 submodule完整落在某卡时才被记录一旦 submodule 被拆buffer 的归属分支被跳过于是 buffer 既没标 GPU0 也没标 GPU1。结果dispatch_model给 parameter 安了家却漏了 buffer。buffer 保持加载时的默认设备通常是 CPU或模型初始化的设备forward 时和已搬到 GPU 的参数做运算device mismatch 爆发。为什么完整 submodule没事因为完整时 buffer 分支正常执行只有拆分这个分支条件没覆盖 buffer才露出破绽。三、根因抽象成代码示意非照抄源码def infer_auto_device_map(model, ...): device_map {} for name, module in model.named_modules(): params_size sum(p.numel() for p in module.parameters()) if params_size 0: dev _assign_device(name, params_size) # 参数分配 device_map[name] dev # BUGbuffer 只在 module 完整落在一张卡时记录 # 若 module 被拆分参数跨卡buffer 分支被跳过 for bname, buf in module.named_buffers(): if not _module_was_split(name): device_map[f{name}.{bname}] dev # 拆分时漏了 return device_map根因链条infer_auto_device_map先给 parameters 分配设备可能把 submodule 拆到两张卡buffer 的归属逻辑依赖该 submodule 是否完整落在单卡的判断当 submodule 被拆分这个判断为 Falsebuffer 归属分支被跳过buffer 没进入device_mapdispatch_model只搬 device_map 里列出的张量buffer 滞留原设备forward 时参数在 GPU、buffer 在别处 - device mismatch。一句话buffer 的分配逻辑没有覆盖submodule 被拆分这一分支。四、最小可运行复现用纯 Python 模拟拆分 submodule 时 buffer 归属被漏掉# repro_buffer_split.py def infer_map(names, split_at): 模拟 device_map 推断参数分配 buffer 归属。 device_map {} split_names set(names[:split_at]) set(names[split_at:]) for n in names: device_map[n] cuda:0 if n split_at else cuda:1 # buffer 归属只在 module 未被拆分时记录 for n in names: if n in split_names: continue # BUG拆分的 module 漏掉 buffer device_map[n .buffer] device_map[n] return device_map def main(): # 假设 submodule block 的参数被拆到 cuda:0 和 cuda:1 names [block.p0, block.p1, block.p2] split_at 2 dm infer_map(range(3), split_at) has_buf any(k.endswith(buffer) and k.startswith(block) for k in dm) print(device_map 含 block 的 buffer, has_buf) assert not has_buf, 拆分 submodule 的 buffer 被遗漏 - 复现成功 if __name__ __main__: main()运行输出device_map 含 block 的 buffer False拆分后的block没有任何 buffer 进入device_map正是真实 bug 的抽象。五、解决方案第一层最小直接修复最小且必须的一步无论 submodule 是否被拆分都给它的 buffer 显式分配一个设备——通常放在持有该 submodule 最后一块参数的那张卡保证 forward 时 buffer 与大部分参数同设备# fix_layer1.py def fix_buffer_assignment(device_map, module_buffers, owner_of_last_param): for bname in module_buffers: # 拆分的 submodulebuffer 放到持有最后参数的卡保证同设备 device_map[bname] owner_of_last_param return device_map更稳妥地直接在infer_auto_device_map的补丁里把 buffer 归属从是否完整改成只要 module 有任何参数被分配就给它 buffer 也分配# 修复buffer 归属不再依赖 module 是否完整 for bname, buf in module.named_buffers(): owner device_map.get(name) or owner_of_last_param device_map[f{name}.{bname}] owner这一层改动最小补上拆分分支的 buffer 归属。但它用最后参数所在卡作为启发式若 submodule 参数均匀分散、buffer 该跟谁并非显然需第二层兜底。六、解决方案第二层结构性改进把buffer 归属做成确定性的规则buffer 跟随其所在 module 的主设备参数总字节数最多的那张卡并由单一函数统一计算不依赖是否拆分的判断# fix_layer2.py from dataclasses import dataclass from typing import Dict, List dataclass(frozenTrue) class TensorRef: name: str bytes: int device: str # 参数被分配到的设备 class DeviceMapBuilder: def __init__(self): self.params: List[TensorRef] [] self.buffers: List[TensorRef] [] def add_param(self, name, nbytes, device): self.params.append(TensorRef(name, nbytes, device)) def add_buffer(self, name, nbytes): self.buffers.append(TensorRef(name, nbytes, ?)) def primary_device_of(self, module_prefix: str) - str: 该 module 参数字节最多的设备作为 buffer 归属。 by_dev: Dict[str, int] {} for p in self.params: if p.name.startswith(module_prefix): by_dev[p.device] by_dev.get(p.device, 0) p.bytes return max(by_dev, keyby_dev.get) if by_dev else cpu def build(self) - Dict[str, str]: dm {p.name: p.device for p in self.params} for b in self.buffers: prefix b.name.rsplit(., 1)[0] dm[b.name] self.primary_device_of(prefix) # 跟随主设备 return dm要点primary_device_of用参数字节最多的设备决定 buffer 归属对拆分 submodule 也确定build对参数和 buffer 统一处理buffer 不再依赖是否完整分支任何拆分情况都落到明确设备forward 不会再 device mismatch。七、解决方案第三层断言 / CI 守护写 pytest 验证拆分 submodule 的 buffer 必在 device_map 中、且与参数同设备# test_device_map_buffers.py import pytest class FakeModel: def named_modules(self): return [(block, None)] def named_buffers(self, prefix): return [(block.running_mean, 1), (block.running_var, 1)] def infer_split_aware(names, split_at): dm {} for i, n in enumerate(names): dm[n] cuda:0 if i split_at else cuda:1 for n in names: dm[n .running_mean] dm[n] # 修复后总是记录 dm[n .running_var] dm[n] return dm def test_split_buffer_in_map(): names [block.p0, block.p1, block.p2] dm infer_split_aware(names, split_at2) assert block.p0.running_mean in dm assert block.p2.running_var in dm def test_buffer_same_device_as_its_param(): names [block.p0, block.p1, block.p2] dm infer_split_aware(names, split_at2) assert dm[block.p2.running_mean] dm[block.p2] def test_no_cpu_stranded_buffer(): names [block.p0] dm infer_split_aware(names, split_at1) for k in dm: if running in k: assert dm[k] ! cpu, buffer 不应滞留 CPUCI 一旦恢复成拆分时跳过 buffer前两个测试立刻变红。八、排查清单forward 报 device mismatch 且怀疑 buffer 时打印device_map搜索出问题的 submodule看其 buffer 是否在 map 中确认该 submodule 是否被拆分到多卡参数跨cuda:0/cuda:1若 buffer 行缺失命中本 bug按第五 / 六节给拆分 submodule 的 buffer 显式分配主设备用dispatch_model后检查 buffer 实际设备next(m.buffers()).device若用device_mapauto确认infer_auto_device_map版本是否已修复把第七节的 pytest 接进 CI守护拆分 submodule 的 buffer 必在 map。九、小结infer_auto_device_map在 submodule 被拆分到多卡时漏掉了该 submodule 的buffer归属——buffer 的分配逻辑只覆盖了module 完整落在单卡的分支拆分时被跳过。结果 buffer 滞留原设备forward 时与已搬走的参数 device mismatch。三层层级第一层拆分 submodule 的 buffer 显式分配到持有其最后参数 / 主设备的卡第二层用DeviceMapBuilder以参数字节最多的设备统一决定 buffer 归属消除拆分分支第三层pytest 验证拆分 submodule 的 buffer 必在 map 且与参数同设备锁进 CI。核心教训凡是按结构切分资源的算法必须保证被切分单元的每一种成员参数、buffer、状态都有归属任何只覆盖完整单元而漏掉被切分单元的分支都是隐性资源错位。
延伸阅读

更多相关文章

2026/9/20 4:42:03

如何3分钟掌握Balena Etcher:镜像烧录的终极安全指南

如何3分钟掌握Balena Etcher:镜像烧录的终极安全指南 【免费下载链接】etcher Flash OS images to SD cards & USB drives, safely and easily. 项目地址: https://gitcode.com/GitHub_Trending/et/etcher 还在为制作启动盘而烦恼吗?担心误操…

2026/9/24 14:41:23

shadcn-vue Dropdown Menu 组件完整指南:安装、API 与实战示例

UI组件前端 【免费下载链接】shadcn-vue Vue port of shadcn-ui 项目地址: https://gitcode.com/gh_mirrors/sh/shadcn-vue 点击查看 免费下载 本指南围绕 shadcn-vue 中的 Dropdown Menu(下拉菜单)组件展开,它是通过按钮等触发器…

2026/9/24 14:41:23

《AI Agent 场景应用 - MobileOpenClaw》第5-4节:初步通过智能体操作手机设备,从意图分析到安卓指令执行的端到端串联

文档教程后端 【免费下载链接】CodeGuide :books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助,请给予支持(关注、…

2026/9/24 14:36:22

【单片机毕设案例分享】基于 STM32 或 51 单片机 SU-03T 语音识别智能窗设计与实现 基于 STM32 或 51 单片机多传感器融合智能遮阳控制系统设计(025608)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于单片机,STM32单片机,51单片机,J…

2026/9/23 12:07:00

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/23 12:06:55

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/24 0:00:21

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:21

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:21

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/22 16:34:32

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/22 20:01:30

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/22 13:25:41

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码