本地跑 Ming-Image-0.1-Design 时报显存不足,或者进程直接被系统杀掉,先别急着换卡、换模型或加内存。把分辨率、批量大小、精度这三个变量分开各测一轮,通常十几分钟就能确定这台机器上的可用边界,之后每次出图按边界取值,就不用反复试错。
先固定提示词与种子,用 nvidia-smi 按固定间隔采样显存,记录每档配置的峰值。分辨率优先测三档,批量从 1 逐级翻倍直到第一次失败,再切换精度对比同一张图的峰值。判断依据是峰值读数、退出码和报错原文,不是手感。没有实测边界前,不建议直接上大批量或长时间挂机跑图。
用 nvidia-smi 盯住一次出图的显存峰值
先拿本机基线:出图前空载读一次,所有对比都以这个读数为基准,否则后面分不清哪部分是模型驻留、哪部分是本次推理的工作区。
# 空载基线
nvidia-smi `--query-gpu`=memory.used,memory.total `--format`=csv,noheader
# 采样间隔 200ms,写入日志,跑图期间保持运行
nvidia-smi `--query-gpu`=timestamp,memory.used,utilization.gpu \
`--format`=csv,noheader,nounits -lms 200 | tee vram.log
# 取峰值
awk -F',' '{gsub(/ /,"",$2); if ($2+0>m) m=$2+0} END{print m" MiB"}' vram.log
采样间隔建议 100~500ms,太稀会漏掉短峰值,太密日志量偏大。峰值记录方式统一用「上面命令行读到的最大值」,不要看工具界面上跳动的瞬时数字。
区分加载完成与推理开始,可以看两个信号:显存曲线先单调爬升然后进入平台,这段是权重驻留;此后 GPU 利用率出现明显抬升、显存再上一个台阶,通常是推理开始。更稳妥的做法是跑一次空跑——只加载模型不推理,或者用极小分辨率、1 步采样出图——得到驻留基线,再跑正式任务,两者差值就是单次出图真正吃掉的工作区。日志里自己的时间戳和 vram.log 的时间戳对一下,就能定位是哪一段涨上去的。
固定提示词与种子,只改分辨率
这一轮只动分辨率,提示词、种子、步数、采样器、批量都保持不变,否则增量归因不到分辨率头上。分辨率取三档:模型常推荐的那一档,往上再取一档,往下取一档保证有一个「肯定能跑通」的参照。
# 每档至少跑两次,第一次可能包含编译、缓存预热
# 用同一句提示词、同一个 seed,只改 width/height
run `--prompt` "<同一句>" `--seed` 1234 `--steps` <固定值> \
`--width` 512 `--height` 512 `--batch` 1
run `--prompt` "<同一句>" `--seed` 1234 `--steps` <固定值> \
`--width` 768 `--height` 768 `--batch` 1
run `--prompt` "<同一句>" `--seed` 1234 `--steps` <固定值> \
`--width` 1024 `--height` 1024 `--batch` 1
记录格式建议直接写成一行:分辨率、批量、精度、峰值显存、结果(成功/失败)、单次耗时。同像素数下换宽高比,占用通常接近;但像素总数翻倍时显存往往不是翻倍——注意力相关的部分可能涨得更快,所以不要按面积线性外推。
批量大小从 1 往上加,找到第一次失败的位置
分辨率定下来后,批量按 1、2、4、8 逐级翻倍。目的是看两件事:批量翻倍时峰值是否近似线性增长;以及在某一档是否提前撞墙(涨得比线性更猛)。撞墙那一档的前一档,就是你这台机器在这套参数下的可用批量。
失败时要记录原文,别只记「报错了」:
- 显存不够:常见形如
CUDA out of memory. Tried to allocate ... GiB (GPU 0; ... GiB total capacity; ...),这类是框架自己抛的,会有完整堆栈。 - 被系统杀掉:没有 Python 堆栈,shell 返回
Killed,进程退出码 137(128+9,被 OOM killer 终止)。此时可用dmesg -T | tail -n 30看有没有 oom-kill 记录。 - 退出码 1 一般是代码或参数错误,不要当成显存问题处理。
# 重试前先确认没有残留进程占卡
nvidia-smi `--query-compute-apps`=pid,process_name,used_memory `--format`=csv
ps -p <pid>
kill <pid> # 仅在确认是上一次残留时执行
清理动作做到「确认卡回到空载基线读数」为止,再跑下一档。反复重跑碰运气,只会把失败位置搅乱。
换精度加载,对比同一张图的占用变化
精度是第三个变量。切换后要复查两件事:可用批量是否变大,画质是否还能接受。下面是通用骨架,具体参数名按你所用框架替换,不要照抄接口名。
# 通用接入骨架:把 dtype 抽成一个可替换配置项
runner = load_pipeline(
model_path = cfg.model_path,
weight_dtype = cfg.dtype, # fp32 / fp16 / bf16,或框架里的量化选项
device = cfg.device,
)
# 或在启动命令上切换
run `--dtype` fp16 `--width` 768 `--height` 768 `--batch` 4 `--seed` 1234
对比方法:同一分辨率、同一批量、同一种子,只换精度,各记一次峰值。画质变化用固定参照图肉眼比对更实际——把不同精度、同种子的输出放进以精度命名的文件夹,逐张看结构、小字、细节纹理是否可接受。降精度换来的批量空间是真实收益还是只是把问题推后,看的是「同一张图在同一批量下能不能稳定跑完」,而不是峰值数字好看。
把结果整理成自己设备的边界表
测完把记录并成一张表,贴在机器旁边或写进仓库的 README,下次出图直接查表,不用重新试。
| 分辨率 | 批量 | 精度 | 峰值显存 | 结果 | 耗时 |
|---|---|---|---|---|---|
| 512×512 | 1 | fp16 | (填实测读数) | 成功 | (填实测值) |
| 768×768 | 1 | fp16 | (填实测读数) | 成功 | (填实测值) |
| 768×768 | 4 | fp16 | (填实测读数) | 失败 / OOM | — |
| 768×768 | 4 | bf16 | (填实测读数) | 成功 | (填实测值) |
使用时在峰值上留一段余量:桌面环境、其他进程、驱动侧的额外占用都会让实际可用显存少于总容量,压着上限跑容易偶发被杀。边界表只在同一台机器、同一套驱动和框架版本下成立,换卡、升级驱动或更新框架后,建议至少重测分辨率和批量这两行。