Skip to Content
Ⅲ · 模型与采样11 · 设备、显存与卸载

11 · 设备、显存与卸载

显存管理不是在 OOM 后调用一次 empty_cache()。ComfyUI 要在多种后端、多个模型和不同 dtype 下,持续决定“谁现在需要、谁可以部分卸载、给推理临时张量预留多少、CPU RAM 是否也快耗尽”。

三种内存不要混成一个数字

VRAM / device memory GPU 等执行设备上的权重、激活与临时张量 CPU RAM offload 权重、缓存中间结果、文件读取 pinned RAM 可更快异步传输,但受注册/锁页预算约束

把权重从 GPU 卸到 CPU 释放了 VRAM,却增加 RAM 压力;把 CPU 权重 pin 住提升传输速度,又减少系统可自由回收的内存。第 08 章的 RAM cache 和本章 model management 必须联动。

设备抽象

model_management.py 在启动时识别可用后端与参数,统一返回 torch.device 或对应包装。该快照覆盖的路径包括:

  • CUDA / NVIDIA;
  • ROCm;
  • Apple MPS;
  • DirectML;
  • Intel XPU;
  • Ascend NPU;
  • MLU;
  • CPU fallback。
comfy/model_management.pyL194-246get_torch_device 与平台分支。

不同设备不仅名字不同:空闲内存查询、支持 dtype、non_blocking copy、缓存清理、pinned memory 和 attention backend 都可能不同。节点应请求全局 device policy,而不是写死 cuda:0。

VRAMState

运行时把策略归纳为 HIGH_VRAM、NORMAL_VRAM、LOW_VRAM、NO_VRAM、DISABLED 等状态,结合 CLI 强制选项和设备能力。

状态直觉
HIGH_VRAM尽量保留完整模型,减少搬运
NORMAL_VRAM在模型驻留与推理余量间平衡
LOW_VRAM只给部分模块预算,更多按需 offload
NO_VRAM极小设备驻留,强依赖 CPU/offload
DISABLEDCPU 等无独立 VRAM 的设备语义

状态不是“实际还剩多少”的替代;每次加载仍查询实时 free memory。

预留内存

代码定义额外 reserved VRAM,Windows 因共享显存行为预留更多,用户可用 --reserve-vram 覆盖。minimum_inference_memory() 还给激活和临时计算保留约 0.8 GiB 加额外 reserve。

comfy/model_management.pyL828-854平台预留与最小推理内存。

若只按权重大小把显存塞满,第一次 forward 仍会因 attention、latent 或 kernel workspace OOM。预留是权重装载与实际可运行之间的安全间隔。

LoadedModel:物理驻留记录

LoadedModel 用 weakref 指向 ModelPatcher,并记录:

  • 目标 device;
  • 实际基础 model 的弱引用;
  • 当前是否被本 Prompt 使用;
  • finalizer;
  • parent patcher 切换关系。
comfy/model_management.pyL733-824LoadedModel 的装载、卸载、部分扩容与弱引用。

它提供三个不同数字:

model_memory() 总模型大小 model_loaded_memory() 目标 device 当前已加载大小 model_offloaded_memory()总大小 - 已加载大小

为什么用 weakref

全局 current_loaded_models 不应仅因登记而让上游 Prompt cache 已淘汰的 patcher 永远存活。对象消失时 finalizer 可清理物理登记;clone 有 parent 时还可切换引用。

load_models_gpu() 的七个阶段

comfy/model_management.pyL901-1016多模型去重、预算、卸载与装载主流程。

1. 去重并纳入 patch dependencies

按输入顺序去重目标 patchers,同时加入 model_patches_models() 返回的相关模型。使用 insertion-ordered dict 而不是 set,避免跨运行随机改变装载顺序。

2. 识别已加载对象

为每个目标构造 LoadedModel 视图,在 current_loaded_models 中寻找同一 patcher。已存在则标记 currently_used。

3. 移除不兼容 clone

共享底层 model 的另一 clone 可能带不同 patches。加载新变体前从当前列表移除冲突 clone,并 detach 旧 patch 状态,避免共享权重同时处于两种逻辑版本。

4. 汇总每个 device 的需求

按设备累计:

total_memory_required = 尚未驻留部分 total_pins_required = 非 dynamic 模型的总权重 pin 需求

汇总后统一 free,比逐模型各自抢内存更稳定。

5. 先释放空间

对每个非 CPU device 请求:

required × 1.1 + max(minimum inference, requested extra) + reserved

不足时 free_memory() 从旧 LoadedModels 中选择可卸载对象。

6. 计算 low-VRAM 预算

NORMAL/LOW VRAM 且非 force full load 时:

current_free = device free + this model already loaded budget = min(可用比例, 扣除最小推理余量后的空间)

NO_VRAM 给极小非零预算,让 Patcher 进入按模块/按权重路径而非误判为“无限预算”。

7. patch + load 并登记

LoadedModel.model_load(budget) 把 patch metadata 移到正确 device/dtype,再调用 patcher partial load。日志分别报告 RAM MiB 与 VRAM MiB,最后把对象插到当前 loaded 列表前方。

free_memory() 怎样挑对象

候选排除 keep_loaded 与 dead model,标记不再 currently used,并按以下信息排序:

  • 已 offload 大小;
  • Python 引用计数;
  • 总模型大小;
  • 当前 loaded list 位置。
comfy/model_management.pyL855-900候选排序、部分卸载、soft cache 清理与 pin 预算。

它先计算还需释放多少;若小于模型已加载量,尝试 partially_unload(),释放够就保留 LoadedModel 记录。否则完全 detach 并从列表移除。

dynamic 模型特例

当本次所有目标都是 dynamic patcher 时,不为另一个 dynamic 模型粗暴卸载 dynamic 模型;它们可通过按需内存控制自行释放。这避免两套管理器互相抖动。

smart memory

默认策略只释放达到目标所需的空间,而不是每次切换都卸载全部模型。DISABLE_SMART_MEMORY 则更激进,在 Prompt 结束时可卸载所有模型。

取舍:

策略优点代价
smart重复工作流快、减少 PCIe 搬运长生命周期、更复杂状态
disable smart行为更易观察、释放彻底每次重新装载,吞吐下降

dtype 决策

unet_dtype() 综合设备能力、模型参数量、受支持 dtype、权重 dtype 与用户开关。should_use_fp16() 等函数包含不同后端的能力和性能例外。

comfy/model_management.pyL1098-1165UNet dtype 与 manual cast 决策入口。

更低位宽并非永远更省或更快:

  • 某设备不原生支持,需要频繁 cast;
  • 某些算子数值不稳定;
  • 量化权重还需要 scale/metadata;
  • 参数省下的显存可能被 cast buffer 抵消。

运行时让 model config 提供允许集合,再让 device policy 选择,而不是节点自行决定。

pinned memory 与异步搬运

CPU offload 的瓶颈可能从容量变成传输。pinned memory 允许更有效的 H2D copy,但 OS 可锁页资源有限。代码维护 pin budget / registry,并在 RAM pressure 时允许 free_pins()。

在支持的设备路径中,还会用独立 stream 进行非阻塞 offload/load,并在正确同步点等待。异步 copy 的正确性不只取决于 non_blocking=True,还要保证源 buffer 生命周期与 stream 依赖。

RAM cache 与模型内存联动

一次节点完成后若系统 RAM 不足:

这说明“低显存”不能只观察 GPU。大量 latent/image 缓存在 CPU,同样会让进程被系统 OOM killer 终止。

OOM 恢复

执行器捕获 model management 识别的 OOM:

  1. 记录内存摘要;
  2. 添加 batch size 等用户提示;
  3. unload_all_models() 清理 loaded 状态;
  4. 把结构化 execution error 发给客户端。

它不能让已失败的 kernel 自动继续,也不会自动降低图像尺寸重试。清理的目标是让下一次 Prompt 不继承破损状态。

常见误区

“模型文件 8 GB,所以 8 GB GPU 足够”

还需要激活、attention、latent、VAE、CLIP、patch/cast buffers 和保留余量;文件量化/压缩大小也不等于加载后大小。

“offload 就是删掉模型”

部分卸载保留 patcher、模块结构和 CPU 权重,只减少执行设备驻留。完全 detach 才更接近卸载逻辑状态。

“empty_cache 能释放所有显存”

PyTorch cache 只释放 allocator 未使用块;仍被 tensor/model 引用的内存不会消失。必须先卸载或删除真实引用。

“节点里手工 .to(device) 更直接”

它绕过 LoadedModel、预算、clone 冲突、dtype、pins 和 partial unload。除纯临时小 tensor 外,模型节点应走统一管理。

调优顺序

1. 确认实际设备与 backend 2. 看日志中的 model total / RAM / VRAM loaded 3. 确认推理 dtype 与 manual cast 4. 区分权重 OOM、采样激活 OOM、VAE decode OOM 5. 降 batch / 分辨率 / tile,观察 minimum inference headroom 6. 再选择 lowvram / reserve-vram / disable smart memory 7. 同时看系统 RAM 与 pinned memory,而非只看 nvidia-smi

可迁移的设计方法

  1. 资源状态用“总量 / 已加载 / 已卸载”连续模型,不用布尔 loaded。
  2. 为执行临时空间预留 headroom,不能把权重塞到 100%。
  3. 多资源池联动回收:VRAM、RAM、pinned RAM 互相转移压力。
  4. 用弱引用/最终器让资源登记不反向制造内存泄漏。
  5. 平台差异集中在 policy 层,业务节点只请求能力。
Last updated on