在双卡 16GB 显存上跑通 LTX Video 2.3 —— 踩坑全记录

4 阅读10分钟

在双卡 16GB 显存上跑通 LTX Video 2.3 —— 踩坑全记录

硬件:RTX 4060 Ti (16GB) + RTX 5060 Ti (16GB),系统内存仅 16GB

目标:跑通 LTX Video 2.3 (22B 参数) 视频生成工作流


背景

LTX Video 2.3 是一个 22B 参数的视频生成模型,完整加载需要 40GB+ 显存。单张 16GB 消费级显卡根本放不下。本文记录了在双 16GB 显卡配置下,从全黑/崩溃到最终跑通的完整排障过程。

最终架构

┌─────────────────────────────────────┐
│  cuda:0 (16GB)                      │
│  ├── Gemma3-12B Q3_K_M GGUF  5.6GB  │
│  ├── 文本投影层 BF16          2.3GB  │
│  ├── Video VAE BF16          1.4GB  │
│  ├── Audio VAE BF16          0.3GB  │
│  └── PyTorch 开销            ~1.5GB │
│  合计: ~11.1GB  余量: ~4.9GB        │
├─────────────────────────────────────┤
│  cuda:1 (16GB)                      │
│  ├── UNet Q4_K_M GGUF       13.3GB  │
│  └── PyTorch 开销            ~1.5GB │
│  合计: ~14.8GB  余量: ~1.2GB        │
├─────────────────────────────────────┤
│  采样激活值峰值: ~1-2GB(cuda:1)   │
└─────────────────────────────────────┘

踩过的坑

坑 1:双卡架构不一致导致 DLPack 跨设备崩溃

两张卡架构不同(一张 Ada,一张 Blackwell),PyTorch 2.10 (cu130) 的 DLPack 跨设备传输检查变严格,不同架构之间没有 P2P 直接访问能力。

解法: 安装官方 ComfyUI-MultiGPU 插件 v2.6.4+,它包含 cu130 DLPack 兼容性修复。

坑 2:NVIDIA-SMI 编号与 ComfyUI CUDA 编号相反

这是个隐蔽的坑:

nvidia-smi 显示实际显卡ComfyUI CUDA 编号
index 05060 Ticuda:1
index 14060 Ticuda:0

教训: 用 ComfyUI 的系统信息页面确认设备编号,不要相信 nvidia-smi。

坑 3:文本编码器缺少投影层 → 维度不匹配 (3840 vs 4096)

现象: CLIP 编码正常,但 UNet 报维度错误:期望 4096 维,实际收到 3840 维。

根因: LTX 2.3 的文本编码器需要两个文件配合使用:

  • gemma_3_12B_it_fp8_e4m3fn.safetensors — Gemma3 基础模型(输出 3840 维)
  • ltx-2.3_text_projection_bf16.safetensors投影层(3840→4096+2048 维)

缺少投影层时,ComfyUI 的 sd_detect() 检测不到投影权重,默认使用 single_linear(输出 3840 维),而 UNet 的 connector 期望 4096 维 → 崩溃。

投影层文件只含 4 个 key,2.3GB:

text_embedding_projection.video_aggregate_embed.weight
text_embedding_projection.video_aggregate_embed.bias
text_embedding_projection.audio_aggregate_embed.weight
text_embedding_projection.audio_aggregate_embed.bias

关键:Qwen3-VL 不是文本编码器的一部分,它是视觉编码器(用于图生视频),文本编码只需要 Gemma3 + 投影层。

坑 4:FP8 CLIP 12.3GB 塞满 16GB 显卡 → OOM

即使 DisTorch2 开启了虚拟显存分摊(virtual_vram_gb),FP8 版 Gemma3(12.3GB)+ 投影层(2.3GB)= 14.6GB,加上 PyTorch 开销直接爆显存。

根因: DisTorch2 的 new_partially_load 函数在加载每个模块时,会先把模块 .to(device) 移到 GPU,打补丁后才移到最终目标设备。峰值显存远高于稳态分配。也就是说,即使最终只有一部分留在 GPU 上,加载过程中的峰值会短暂占满整张卡。

解法: 换用 GGUF 量化版本 gemma-3-12b-it-abliterated-v2.q3_k_m.gguf(5.6GB),总量从 14.6GB 降到 7.9GB,峰值也不会爆。

坑 5:GGUF + 非 GGUF 混用时 GGMLOps 崩溃

用 GGUF 版 Gemma3 + safetensors 投影层时,ComfyUI-GGUF 插件会强制用 GGMLOps 构建整个模型,包括非量化的投影层。GGMLOps 的 Linear 层把 weight 初始化为 None,通过专门的加载函数填充。如果加载逻辑有 bug(早返回跳过投影层),cast_bias_weight 就会因为 weight=None 崩溃。

解法: 修复 ComfyUI 的 lt.pyLTXAVTEModel.load_sd() 的早返回 bug —— 原代码遇到 Gemma3 的 key 时直接 return,跳过了投影层加载。

坑 6:Q4 量化导致运动区域块状伪影

现象: 视频能生成,但运动部分出现马赛克/块状碎裂,静态区域正常。

根因: UNet 使用 Q4_K_M(4-bit)量化,运动区域对时序精度要求最高,4-bit 量化在这块最先崩溃。

影响层级:

  • CLIP 量化(Q3)→ 影响提示词理解力,不影响画面质量
  • UNet 量化(Q4)→ 直接影响画面像素质量,运动区域最先出问题

缓解: 可以换用全精度 UNet(21GB)配合 DisTorch2 跨卡分摊,但配置复杂。或者接受 Q4 的画质折中。

坑 7:soft_empty_cache 补丁导致图片生成时反复卸载重载模型

这是从 LTX 视频排障延续到图片生成时的一个隐蔽问题。

现象: 跑 Krea2 图片生成时,每次 prompt 之间都会卸载全部模型(UNet + CLIP + VAE)再重新加载。单次推理只需 8 步 ~3 秒,但模型重新加载耗时数分钟,导致批量生成效率极差(479 秒/张 vs 正常 35 秒/张)。

根因: MultiGPU 插件在 device_utils.py 中 patch 了 ComfyUI 的 mm.soft_empty_cache,替换为 soft_empty_cache_distorch2_patched。这个补丁函数内有一段无条件的 CPU 内存检查

def soft_empty_cache_distorch2_patched(force=False):
    is_distorch_active = False
    for lm in mm.current_loaded_models:
        if hasattr(inner_model, '_distorch_v2_meta'):
            is_distorch_active = True
            break

    # ⚠️ 无条件执行,不管 DisTorch V2 是否活跃
    check_cpu_memory_threshold()  # RAM > 85% 就触发 free_memory = True
    ...

ComfyUI 标准流程是每条 prompt 跑完后都调一次 soft_empty_cache。这段补丁使得:

每条 prompt 跑完
  → soft_empty_cache 被调用
  → check_cpu_memory_threshold() 无条件检查 RAM
  → 16GB RAM 跑 12GB FP8 模型时必然 > 85%
  → 设置 free_memory = True
  → 下一条 prompt 进来 → 卸载全部模型 → 重新加载
  → 循环 ♻️

check_cpu_memory_threshold() 是为 DisTorch V2 视频模式设计的(视频生成时模型在多卡间搬运,确实需要管 RAM),但图片生成场景完全不需要。

解法: 将 CPU 内存检查限制为仅在 DisTorch V2 活跃时执行:

    # 仅 DisTorch V2 视频模式需要,避免图片生成时反复卸载模型
    if is_distorch_active:
        check_cpu_memory_threshold()

修改文件: custom_nodes/comfyui-multigpu/device_utils.py(已备份 .bak

教训:

  • CPU_MEMORY_THRESHOLD_PERCENT 默认 85%,对 16GB 系统内存极为苛刻。任何 > 12GB 的模型加载经过 RAM 时都会触发
  • 这个问题在用 NVFP4 模型(7GB)时不明显(RAM 偶尔超阈值),换成 FP8 模型( 12GB)后每条 prompt 必然触发
  • MultiGPU 插件的内存保护逻辑是为视频生成(DisTorch V2)设计的,但被无条件应用到了所有场景,对纯图片生成造成副作用

坑 8:ComfyUI Desktop 最小化窗口导致每条 prompt 后自动卸载模型

现象: ComfyUI Desktop 窗口在最前时,模型加载后持续驻留显存,批量生成流畅。但一旦最小化窗口(或切换到其他窗口),每条 prompt 执行完后都会自动卸载所有模型(UNet + CLIP + VAE),下一条 prompt 又要重新加载,导致批量生成时间暴增。

根因: ComfyUI Desktop 基于 Electron (Chromium)。当窗口最小化时,Chromium 的 Page Visibility API 检测到页面不可见,触发了 ComfyUI 前端的自动内存释放逻辑——前端调用 api.freeMemory() 向后端发送 POST /free 请求,后端设置 unload_models flag,导致所有模型在 prompt 执行完成后被卸载。

完整触发链:

窗口最小化 → document.hidden = true
  → 前端触发 api.freeMemory()
  → POST /free { "unload_models": true }
  → server.py: prompt_queue.set_flag("unload_models", True)
  → main.py: comfy.model_management.unload_all_models()  💥
  → 下一条 prompt → 重新加载全部模型(数分钟)

后端关键代码(main.py 第 384-386 行):

flags = q.get_flags()
free_memory = flags.get("free_memory", False)
if flags.get("unload_models", free_memory):
    comfy.model_management.unload_all_models()

解法:

  • 批量生成时不要最小化 ComfyUI 窗口,保持窗口可见(可以在另一个显示器上放着)
  • 如果只有一个显示器,把窗口缩小、移到角落,但不要最小化
  • 如果是通过 API 批量提交(如 batch_generate.py),可以在生成期间不打开 ComfyUI 的浏览器界面

教训: 这是 Electron 应用的常见副作用——窗口不可见时"优化"系统资源,但对需要持续驻留大量模型在显存的 AI 推理场景,这种优化适得其反。


关键经验总结

1. 显存预算要算 PyTorch 开销

理论计算模型大小时,务必预留 ~1.5-2GB 给 PyTorch runtime(CUDA context、kernel cache、临时缓冲区)。很多 OOM 不是模型太大,而是忘了这部分开销。

2. DisTorch2 的峰值 ≠ 稳态

DisTorch2 分摊加载时,峰值显存可能远超最终稳态分配。选择 virtual_vram_gb 参数时,不能只看稳态,要给峰值留余量。

3. 量化精度选择

组件推荐精度原因
CLIP(文本编码器)Q3/Q4 GGUF 可接受只影响语义理解,不影响像素
UNet(核心生成)尽量用高精度直接决定画面质量
VAEBF16/FP16解码质量不能妥协

4. 组件职责隔离排障

遇到画面问题时,按职责定位:

  • 内容不对(生成的东西跟提示词不符)→ 查 CLIP
  • 画质不行(模糊/碎裂/伪影)→ 查 UNet 精度和采样参数
  • 颜色/解码异常 → 查 VAE

5. ComfyUI Desktop 的工作流陷阱

ComfyUI Desktop 运行时,浏览器中保存着工作流的内存副本。直接修改磁盘上的 JSON 文件不会生效——下次关闭 Desktop 时,它还会用内存中的旧版本覆盖你的修改。所有参数调整都应该在 ComfyUI 界面内完成。

6. MultiGPU 插件的内存保护对图片生成是副作用

MultiGPU 插件 patch 了 soft_empty_cache,加入了 CPU RAM 使用率检查(默认阈值 85%)。这个机制对 DisTorch V2 视频模式是必要的(视频生成时模型要在多卡间搬运,RAM 压力大),但对普通图片生成会造成每条 prompt 之间卸载重载模型。

16GB 系统内存是触发条件: 任何 FP8 级别(12GB+)的模型加载经过 RAM 时都会撑破 85% 阈值。如果系统内存 ≥ 32GB,这个问题基本不会出现。

排查方法: 如果日志里反复出现 Triggering PromptExecutor cache reset. Reason: cpu_threshold_exceeded,且每条 prompt 都有 Requested to load Krea2,就是这个问题。

7. 量化精度影响 RAM 过路压力

模型从磁盘加载到 GPU 时,必须先经过系统内存(RAM)。量化精度不仅影响显存占用,还直接影响 RAM 过路压力:

量化格式模型体积16GB RAM 压力效果
BF16~24GB💥 爆炸不可用
FP8~12GB⚠️ 极限触发反复卸载
NVFP4~7GB✅ 可控流畅
Q4 GGUF~5GB✅ 轻松流畅

8. ComfyUI Desktop 最小化窗口会触发模型卸载

ComfyUI Desktop 基于 Electron,窗口最小化时会触发前端的自动内存释放,导致每条 prompt 执行完后卸载所有模型。批量生成时务必保持窗口可见。

9. 模型卸载问题的排查清单

当遇到"每条 prompt 之间都重新加载模型"的问题时,按以下顺序排查:

  1. ComfyUI 窗口是否最小化? → 恢复窗口(坑 8)
  2. MultiGPU 插件是否触发了 CPU 内存检查? → 查日志有无 cpu_threshold_exceeded(坑 7)
  3. 前端是否在发送 /free 请求? → 在浏览器 Network 面板监控
  4. 是否有多余的 Loader 节点? → 清理工作流中的废弃节点

系统内存的重要性

本案中系统内存仅 16GB,这成为了另一个瓶颈。DisTorch2 的 offload 目标是系统内存(CPU),当 offload 量超过空闲内存时,操作系统开始换页(swap),虽然不会崩溃但速度极慢。

如果条件允许,32GB 以上系统内存会让多 GPU 显存分摊方案更加从容。


本文基于 ComfyUI v0.27.0 + PyTorch 2.10.0+cu130 环境,2025 年 7 月。