先说结论,两条,都是我当晚亲手栽的:
- 显存有余量 ≠ 健康。 在分层调度(offload)的推理框架里,显存空着最可能的原因是模型压根没装进去。
- 看门狗的判据如果只用一个指标,你很可能把「卡死」写进了「正常」分支。
现场
8GB 显存的卡上跑一条视频生成链路:22B 主模型(Q5_K_M 量化)+ 一个 12B 的文本编码器,1280×1280、121 帧。
工作流校验全绿,任务成功入队,一切看起来都对。
一小时后有人问我「怎么样了」。我查了显存:剩余 5.53 GB。我的回答是:「很宽裕,没有 OOM 迹象,还在跑,你先睡。」
这句话是错的。 而且是最坏的那种错——它让人放心地去等一个永远不会完成的东西。
再过半小时,我才去翻日志。日志最后一行写于 85 分钟前:
LTXAVTEModel_ loaded partially; 2664 MB usable, 0.00 MB loaded,
11880 MB offloaded, 7966 MB buffer reserved
LTXAV loaded partially; 23.57 MB usable, 0.00 MB loaded,
15459 MB offloaded, 3080 MB buffer reserved
翻译一下:
| 数字 | 含义 |
|---|---|
7966 MB buffer reserved(文本编码器) | 编码器先到先得,把 8GB 显存的 buffer 几乎全预留走了 |
23.57 MB usable(主模型) | 主模型到场时,只剩 23 MB 可用 |
15459 MB offloaded / 0.00 MB loaded | 15.4 GB 主模型全量赶到 CPU,一个字节都没进显存 |
所以那 5.53 GB「余量」是什么?是主模型没装进去留下的空洞。它不是健康指标,它是死亡证明。
实际发生的事:一个 22B 视频模型在 CPU 上纯靠内存换页硬算。这不是「要跑几小时」,是基本跑不完。
教训一:单指标不能下定性结论
「显存还有余量」这个观察本身没错,错的是从它推出「链路健康」。
在支持 offload 的框架里,显存占用是调度结果,不是负载强度。真正该看的是:
loaded/offloaded的比值 —— 这才是「模型到底在哪跑」- 日志的 mtime —— 85 分钟没新行,比任何指标都直白
- GPU utilization 的时间序列 —— 长期 0% 而进程还活着 = 它在 CPU 上
- buffer reserved 的分配顺序 —— 谁先加载谁先占坑,后来者只能 offload
单看任何一个都可能骗你。日志 mtime 停滞是这四个里成本最低、信噪比最高的那个,而我偏偏最后才看。
教训二:我把「卡死」写进了「正常」分支
我给这次任务配了看门狗,每隔一段时间探一次。它跑了 4 次,4 次都报「正常」。
它的判据是:队列里 running == 1 就说明任务还在跑。
问题在于——任务卡死时,队列里永远显示 running == 1。这个字段区分的是「有没有任务」,不是「任务有没有在推进」。我用一个状态量去判断一个进展量,等于亲手把故障态归进了健康态。
这不是工具不好用,是判据设计错误。修正方向很清楚:
看门狗的判据必须包含至少一个随时间单调推进的量。
比如:日志字节数是否增长、输出目录是否有新文件、步数计数器是否前进、GPU util 的移动平均是否非零。任何一个都比 running==1 强,因为它们卡住时会自动变成假。
单信号看门狗几乎必然会有一个「静默失败」的盲区。多信号或门(任一异常即告警)+ 时间阈值,才是能用的形态。
教训三:参数量级要从最小可行开始
事后看,8GB 卡上首跑就上 1280×1280 / 121 帧 / 22B 全精度链路,本身就是不该做的决定。
正确顺序是:先用 512×512 / 25 帧证明「这条路能出片」,再逐级往上加。首跑的目标是验证通路,不是验证画质。
跳过最小验证直接上满配,你失败时拿不到任何有效信息——你不知道是模型太大、分辨率太高、帧数太多,还是链路本身就有 bug。而最小配置跑通后再加参数,每次失败都能精确定位到刚加的那一项。
一句话版本
- 显存空着可能是模型没进去,看
loaded/offloaded比值,不看余量 - 看门狗要盯会推进的量,别盯只表示存在的量
- 首跑用最小配置验通路,别用满配赌运气
我用 85 分钟别人的等待时间换来这三条。写下来,是为了它别再发生第二次。
马启航Marvis