← 全部文章

本地出图环境怎么攒起来的:显存账、隐藏窗口与就绪判定

前两篇分别讲了部署一个新模型、以及用这个模型跑的出图迭代。这篇补最后一角:环境本身。

单卡(RTX 5090,32GB)本地跑出图模型,装包不是难点。难点是四件事:

  1. 怎么让它可重复地起来;
  2. 怎么判定它真的就绪了;
  3. 怎么干净地停下(以及为什么在 Windows 上”杀进程”是一件事);
  4. 显卡上一边有本地大模型常驻的时候,显存账怎么算。

一、启动:三个 flag + 一个必须关掉的进度条

这台机器上 ComfyUI 的启动脚本原本是 .cmd,但从 git-bash 调它踩了 MSYS 的参数转换坑(cmd //c 不会按预期执行)。最后改成直接用 Python 起进程,最稳:

subprocess.Popen(
    [r"D:\ComfyUI\venv\Scripts\pythonw.exe", "-u", "main.py",
     "--listen", "0.0.0.0", "--port", "8188", "--enable-manager"],
    cwd=r"D:\ComfyUI", env=env,
    stdout=log, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL,
    creationflags=subprocess.CREATE_NO_WINDOW | subprocess.DETACHED_PROCESS
                    | subprocess.CREATE_NEW_PROCESS_GROUP,
    close_fds=True,
)

几个不可省的:

  • pythonw.exe + CREATE_NO_WINDOW + DETACHED_PROCESS + CREATE_NEW_PROCESS_GROUP —— 四个一起,才能真正做到”完全隐藏窗口且不随父进程退出”。少任一个都可能弹窗,或者父进程一退它就跟着死。
  • stdin=DEVNULL / close_fds=True —— 不然子进程会继承句柄,父进程的行为会莫名影响它。
  • 环境变量 TQDM_DISABLE=1 —— 这是 Windows 上一个很隐蔽的坑:采样循环里的进度条在某些情况下会让 KSampler 抛 Errno 22。另外还要带 PYTHONUTF8=1 和 VHS_FORCE_FFMPEG_PATH(视频节点用)。

二、就绪判定:别等”日志看起来好了”

最省事的判定是 curl 一个状态码:

curl -o /dev/null -w "%{http_code}" http://127.0.0.1:8188/

启动到就绪大约 40–60 秒(第一次加载模型另算)。不要用”睡 60 秒”代替它就绪判定——模型大小、缓存命中、磁盘速度都会让这个时间浮动。

三、停止:在 git-bash 里杀进程要绕一个弯

Windows 上 taskkill 本身没问题,但从 git-bash 直接写 taskkill //PID 会被 MSYS 的参数转换弄坏。正确写法是把参数转换关掉:

MSYS2_ARG_CONV_EXCL='*' taskkill /PID <pid> /F

这条跟前面 cmd //c 那个坑是同一类问题:MSYS 会把看起来像路径的参数自动转换,//PID、/c 这类都会被它”帮忙”改掉。规律是——给原生 Windows 程序传形如 /X 的参数,要么关掉转换,要么用编程接口绕开命令行。

四、显存账:出图模型和本地大模型能不能同时站着

这台机器上还常驻着一个本地推理服务(llama.cpp 的 router 模式,监听本机端口,供游戏/知识库用)。于是有个必须回答的问题:出图的时候要不要先把本地模型停掉?

记一次实测的结论:不需要腾显存。

机制上是两件事凑在一起:

  1. 本地推理服务是”按需加载”的——没有请求时不占显存,空闲一段时间(这里配的是 300 秒)自动卸载权重;所以它平时的显存占用是接近 0,而不是”模型常驻”。
  2. 出图侧的分配策略 —— 当前 ComfyUI 版本在这张卡上是 cudaMallocAsync 分配,外加 RAM pressure cache。显存不够时,它会把权重放在内存里、按需在显存里进进出出,而不是直接 OOM。

实测最坏的一档是:本地大模型正好满载占着卡,剩余显存只有 854MiB,出图仍然跑完了——代价是单张从十几秒涨到 94 秒(权重被反复换入换出)。

所以显存账的正确算法不是”出图模型要多少 GB,够了才开”,而是:

峰值需求 = 文本编码器 + DiT 同时在显存里的那一段;不够的时候不会崩,只是会变慢,慢多少取决于要换多少次。

这个认知直接改变了使用习惯:不用再手动停服务。代价只是慢,而慢是可接受的;手动停服务带来的失败面(忘了重开、正在被别的任务用、重启顺序错)反而更大。

五、版本:模型、工作流、输出都在版本控制之外

这台机器的 ComfyUI 是 git 管理的(所以能 cherry-pick 上游 PR,也能整体升到 master)。升级时最担心的永远是”我的东西会不会被弄没”——这里的答案是:

模型目录、工作流、custom_nodes、output 全在 .gitignore 里,版本切换过程零接触。所以升核心是安全的动作,前提是确认这三件事:

  1. 依赖跟着 requirements.txt 一起升(前端、comfy-kitchen 这类包版本会和核心耦合);
  2. 升完立刻拿一个已知能跑的模型重新冒烟出图,别只看接口 200;
  3. 升级前记下当前 commit —— 出问题时能退回去。

六、这套环境最后长成什么样

  • 常驻:出图服务监听 8188(隐藏窗口、脱离进程树),本地推理服务按需加载。
  • 启动:Python 直接起进程(绕开 .cmd 解析),40–60 秒就绪,用状态码判定。
  • 停止:MSYS2_ARG_CONV_EXCL='*' taskkill /PID <pid> /F。
  • 显存:两者共存,不手动腾;显存紧张时表现为变慢而不是失败。
  • 版本:核心可升可退,用户资产在 .gitignore 之外,升级零接触。

花在这上面的时间,比装那个模型本身多。但它是后面所有出图工作的地基——一个起得来、判得准、停得干净的环境,才谈得上”调参数”。