显存吃紧时,先降分辨率,而不是先减帧。分辨率通常直接决定单帧张量大小,是显存峰值的底座;帧数和帧生成批次更多影响运行过程中的驻留与累积。顺序反了容易出现“帧数减到很少仍然 OOM”的情况:单帧本身就放不下,减帧只是缩短了跑多久,不解决放不放得下。可操作的做法是固定其他变量,先只把分辨率往下降,找到能稳定出画面的最低一档,再在这档上把帧数与批次调到够用。
适用场景:单卡消费级显卡跑 Matrix-Game 4.0,出现显存不足或跑到一半中断。操作动作:复制一份配置,固定输入(同一段素材、同一时长、同一种子),只改分辨率一个键跑一轮,记录峰值与运行时长;再只改帧数或批次跑一轮。验证方式:用 nvidia-smi 轮询记录显存曲线,看峰值出现的位置是启动瞬间还是运行途中。风险边界:降分辨率会直接损失画面细节,减帧会缩短可用时长,两者都不能靠调参换回;若某一档触发显存错误,就回退上一档并把它记为边界值。
在配置文件里找出影响显存的三类参数
先确认改动落在真正生效的那份配置上。Matrix-Game 类项目一般把配置放在仓库的 configs/ 或 conf/ 目录,也可能由启动命令的 `--config` 参数指定。用下面两条命令定位文件并列出候选字段,注意输出的是你本地文件里确实存在的键,不要凭印象往里加。
cd <你的仓库目录>
ls configs conf 2>/dev/null
grep -rnE '(resol|width|height|size|frames|num_frames|batch|chunk|window|cache|dtype|offload)' configs/
把 grep 出来的键按用途分三类,写进记录表再开始改:
- 分辨率类:如
width/height、resolution、image_size之类,决定单帧张量形状。 - 帧生成批次类:如
num_frames、batch_size、frame_chunk、chunk_size,决定一次生成多少帧、分几批推进。 - 缓存与精度类:如
dtype、cache、offload、vae_slicing一类开关,影响中间结果是否留在显存。
名字相近但与显存无关的项要排除,例如日志里的 log_every、输出目录里的 frame_dir。改完后打印一次合并后的运行时配置(多数项目支持 `--dump-config` 或在启动日志开头打印),确认生效的就是你以为的那一项;只看 YAML 文件很容易被默认值覆盖。
固定其他变量,先只改分辨率
这一轮只允许分辨率变化:同一份输入、同一时长、同样的批次与精度设置,其余键一个不动。启动生成之前,先在另一个终端开一个显存采样循环,然后把采样文件和生成日志放在同一个目录方便对照。
# 终端 A:先记录空载基线
nvidia-smi `--query-gpu`=memory.used `--format`=csv,noheader
# 终端 A:开始采样,间隔 500ms,输出到文件
nvidia-smi `--query-gpu`=timestamp,memory.used,utilization.gpu \
`--format`=csv,noheader -lms 500 | tee mem_res.csv
# 终端 B:照常启动一次生成
python -m <你的入口> `--config` configs/<你的配置>.yaml
采样间隔建议 200–500ms,太粗会漏掉启动瞬间的峰值,太密则文件膨胀。生成结束后用 Ctrl+C 停掉采样,取峰值:
cut -d, -f2 mem_res.csv | tr -dc '0-9\n' | sort -n | tail -1
把每档分辨率的结果按同一格式记下来,“峰值”一列要同时写空载基线,否则下次换了桌面环境就对比不了:
| 批次 | 改动项 | 旧值 → 新值 | 其余项 | 空载基线 | 峰值显存 | 运行时长 | 报错/画面 |
|---|---|---|---|---|---|---|---|
| R1 | 分辨率 | 原始 → 第 1 档 | 不变 | 待记录 | 待记录 | 待记录 | 待记录 |
| R2 | 分辨率 | 第 1 档 → 第 2 档 | 不变 | 待记录 | 待记录 | 待记录 | 待记录 |
如果两档之间峰值几乎没变,说明分辨率不是当前的限制项,先别继续往下砍,转去查帧数与批次。
再只改帧生成或批次相关参数
回到上一轮能出画面的分辨率,把分辨率锁死,只动 num_frames 或 chunk_size。这一轮要回答的是:显存被单帧占住了,还是被运行过程中的累积占住了。判断方法是看峰值出现的位置和运行时长。
# 记录运行时长,生成结束后立刻再读一次显存
start=$(date +%s)
python -m <你的入口> `--config` configs/<你的配置>.yaml
echo "elapsed: $(( $(date +%s) - start ))s"
nvidia-smi `--query-gpu`=memory.used `--format`=csv,noheader
- 峰值在启动后很短时间内到达平台、之后基本持平,且减半批次峰值明显下降:属于单帧或单批占用主导,继续减批次有效。
- 峰值随运行时长缓慢爬升,结束那一刻读数最高:属于中间结果或缓存累积,减帧数只能延缓,不能根治,需要同时确认缓存释放相关开关。
- 帧数减到很少仍然报显存错误:瓶颈不在帧数,回到分辨率那一步继续降。
对比时把改动前后的峰值读数和运行时长成对写进同一张表,只写峰值不写时长会误判:时长缩短导致峰值下降,并不等于单帧变小了。
把两组结果合并成可跑区间
把分辨率那一列和批次那一列并排看,取交集。推荐组合的构造方式是:分辨率取“能稳定出画面”的最低一档,帧数与批次取“能覆盖你需要的最长时长”的最小值,并在峰值上留出余量——建议不低于空载与峰值差值的十分之一,或者留几百 MB,具体看你的桌面环境占多少显存。
# 推荐组合示例(键名以你本地 grep 结果为准)
resolution: <能稳定出画面的最低档>
num_frames: <覆盖所需时长所需的最小帧数>
chunk_size: <缩到不再抬高峰值的最小批次> # 若配置里没有此键则跳过
dtype: <维持原有设置,不在这一轮改动>
边界值同样要写清楚:把失败那次的参数记成“上限越界点”,例如某分辨率下批次取到某一档出现显存分配失败,那么这一档就是边界,实际使用取它下面一档。边界值比推荐值更有用,因为下次换输入时你能立刻知道还有多少余量。
用同一输入复跑两次确认稳定
一次成功可能只是上一次进程没退干净、显存刚好腾出来了。用完全相同的输入和参数连跑两次,两次之间确认没有残留进程:
nvidia-smi `--query-compute-apps`=pid,process_name,used_memory `--format`=csv
# 若有上一次留下的进程,先确认归属再结束它,不要直接 kill 陌生 PID
两次各存一份采样文件(如 mem_run1.csv、mem_run2.csv),分别取峰值和运行时长。判断标准是:两次峰值接近、都低于你设定的余量线、两次日志里都没有显存分配失败或 OOM 关键字、两次输出画面在帧数和内容上一致。若第二次峰值明显高于第一次,说明存在未释放的缓存或临时占用,这个组合还不能算可跑区间,需要重启运行时后再复测。若两次都出现同一条警告但画面正常,也把它记进表格,避免下次误判为回归。