能不能在你自己的机器上跑 Matrix-Game3.5 的实时流式交互,通常不取决于“装没装上”,而取决于两个数:加载后权重常驻占多少显存、逐帧推理时峰值冲到哪里,以及单帧端到端要多久。这两个数没量出来,分辨率和采样步数就只能靠猜;量出来之后,参数怎么设是能被日志和 nvidia-smi 读数支撑的。下面这套流程建议在正式接流式交互之前走一遍,用最小一帧把底摸清。
适用场景:单机单卡、想跑 Matrix-Game3.5 流式交互,但不确定显存和单帧耗时是否够。操作动作:先用最小配置跑通单帧,分别记录加载完成后常驻显存、推理峰值显存,以及加载、前向、后处理三段耗时。验证方式:同一组参数复跑多次,看峰值与耗时是否收敛。风险边界:一帧的数据不能代表长时间连续交互的行为,缓存与上下文累积需要另测;具体可用阈值以本机日志和 nvidia-smi 读数为准。
在仓库说明和依赖文件里核对显存与运行环境的硬性条件
这一步的目的很单纯:确认本机落在可运行范围内,避免模型权重下载了几个小时才发现卡不对。打开仓库的 README、requirements.txt、environment.yml 或 pyproject.toml,把所有跟显卡显存、CUDA 版本、PyTorch 版本、Python 版本相关的句子原文抄出来,一条一条对本机的情况。抄原文而不是凭印象记,是因为“建议 24G 以上”和“需要 ≥24G”是完全不同的事。
本机侧的读数可以先用这几条命令拿到,再和仓库要求逐项对齐:
nvidia-smi `--query-gpu`=name,memory.total,driver_version `--format`=csv
nvcc `--version`
python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())"
python -c "import torch; print(round(torch.cuda.get_device_properties(0).total_memory/1024**3, 1), 'GiB')"
核对结果建议直接落成一张表,方便回头查:
- 仓库原文要求(逐字抄):显存 / CUDA / PyTorch / Python,各一行。
- 来源位置:哪个文件、哪一段,方便复核。
- 本机对应项:上面的命令实际输出。
- 是否满足:满足、边缘、不满足三档,别只写“差不多”。
不满足的项单独列出来,不要混在正文里被忽略。常见的几类:显卡总显存刚过线但桌面环境已占用一部分,实际可用显存会偏小;PyTorch 自带的 CUDA 版本和驱动支持的版本不匹配;仓库锁定了某个 Python 小版本而这些依赖在本机是另一个版本。这几类里,显存和 CUDA 版本属于必须先解决的问题,Python 版本通常在依赖安装阶段就会暴露。
用一条最小命令跑通单帧推理
不要一上来就接流式 pipeline。先用单帧模式跑通一次,把“环境问题”和“性能问题”分开。先看仓库入口脚本的参数说明:
python <入口脚本> `--help`
再按 help 里实际的参数名拼命令。下面是一个通用骨架,参数名需要替换成仓库里真实存在的项:
python <入口脚本> \
`--ckpt` /path/to/weights \
`--input` /path/to/first_frame.png \
`--num`_inference_steps 20 \
`--output` ./out_one_frame \
`--precision` fp16
跑通判据看三件事,缺一不算:日志里出现权重加载完成的提示(常见形态是打印加载耗时或模型参数量)、生成一行指向产出文件的路径日志、输出目录里确实有文件且能正常打开、尺寸与输入对得上。进程退出码为 0 也要确认一下,有些错误会被吞掉但仍写出空文件。
如果这一步就失败,先别调分辨率和步数,按报错回到上一节核对环境:CUDA 不可用、显存不足、权重路径不对,这三类占了大部分。单帧都跑不通的时候,后面的耗时数据没有意义。
在推理脚本里给加载、前向、后处理分别打时间戳
端到端单帧耗时只能告诉你“慢”,不能告诉你“哪里慢”。在推理脚本里插三个打点,把加载、前向、后处理拆开。注意 CUDA 是异步执行的,不在打点前后加同步,前向时间会明显偏小,甚至小到不合理:
import time, logging
t0 = time.perf_counter()
pipe = load_pipeline(ckpt_path, precision="fp16") # 替换成仓库实际的加载入口
torch.cuda.synchronize()
t1 = time.perf_counter()
frame = pipe(input_image, num_inference_steps=steps) # 替换成实际推理调用
torch.cuda.synchronize()
t2 = time.perf_counter()
out = postprocess(frame) # 替换成实际的解码/后处理
torch.cuda.synchronize()
t3 = time.perf_counter()
logging.info("LOAD %.1f ms | FWD %.1f ms | POST %.1f ms | TOTAL %.1f ms",
(t1-t0)*1000, (t2-t1)*1000, (t3-t2)*1000, (t3-t0)*1000)
三个时间戳对应的分工是:t0→t1 覆盖权重读取、精度转换、搬到显存;t1→t2 是纯前向,也就是受分辨率和采样步数直接影响的那一段;t2→t3 是解码成图或转成可输出格式。日志里按上面的格式每帧打一行,连续看十几行就能判断稳定值。
有两个坑需要提前知道。首次运行包含算子编译或 kernel autotune,前一两帧通常明显偏慢,统计时把热身帧丢掉,只看后面稳定下来的行。另外后处理如果做成了异步或延迟执行,同样要加同步点,否则耗时会被算到下一帧头上。
用 nvidia-smi 连续采样显存,标出峰值出现阶段
显存要区分两件事:权重加载完之后常驻吃掉多少,以及逐帧推理时会不会额外冲高。前者决定你最低需要多大的卡,后者决定你在流式交互里能留多少余量。在跑推理的同时另开一个终端连续采样:
nvidia-smi `--query-gpu`=timestamp,memory.used,memory.total,utilization.gpu \
`--format`=csv -lms 200 | tee mem_log.csv
如果驱动不支持 timestamp 字段,换成不带该字段的版本,或者用循环采样:
while true; do
nvidia-smi `--query-gpu`=memory.used,utilization.gpu `--format`=csv,noheader
sleep 0.2
done | tee mem_log.txt
采样间隔建议 100–200 毫秒。间隔太粗会漏掉短时峰值,太细则日志本身带来额外开销。采样时间要盖住完整的启动到退出过程,否则看不到加载阶段的爬升。
从日志里取四个读数记下来:加载前的基线、加载完成后的常驻值、第一次前向之后的读数、连续跑到第十帧以后的稳定值。然后标注峰值出现的阶段:如果峰值出现在加载阶段并随后回落,多半是权重加载时的临时缓冲区;如果峰值出现在第一两帧并回落,通常是编译和缓存;如果峰值出现在每一帧且不回落,那才是真正的推理峰值,需要按它来定分辨率。这个区分直接决定后面参数怎么调。
把三组分辨率与步数的结果写进同一张对照表
有了单帧耗时和显存峰值,再把分辨率和采样步数作为变量扫一遍。建议从最小分辨率、最少步数起跑,逐级往上加,一组跑完再换下一组,不要同时改两个以上变量。每组参数至少复跑 3 次,在表里写清楚复跑次数,取中间值或者记成区间,单次读数容易被系统负载带偏。
| 分辨率 | 采样步数 | 显存峰值 (MiB) | 稳定显存 (MiB) | 单帧耗时 (ms) | 是否 OOM | 复跑次数 |
|---|---|---|---|---|---|---|
| (最低档) | 否 | 3 | ||||
| (中档) | 否 | 3 | ||||
| (较高档) | 待填 | 3 |
OOM 那一列不要只写“是”或“否”,出现 OOM 时补一句发生在哪一步:加载阶段、第一帧、还是跑到第几帧才崩。这三种情况的处理方式完全不同——加载阶段崩说明权重本身就放不下,跑到若干帧后崩更可能是缓存或上下文在累积,而后者恰恰是单帧测试发现不了、需要在流式模式下再确认的问题。
选参数组合的判断方式可以这样:先排除出现 OOM 或耗时抖动很大的组,再看剩余的组里单帧耗时能否塞进你预期的交互帧间隔并留出余量,最后在满足这一条的组合里挑分辨率更高的。余量留多少要看你的输入输出链路,不能只看模型本身;这一点需要在自己的完整链路上再测一次才算数,单帧数据只是入场判断。