ComfyUI 产线实战:从单节点到全流水线
2026/9/21 19:45:17 网站建设 项目流程

ComfyUI 产线实战:从单节点到全流水线

【免费下载链接】ComfyUIThe most powerful and modular diffusion model GUI, api and backend with a graph/nodes interface.项目地址: https://gitcode.com/GitHub_Trending/co/ComfyUI

ComfyUI 是一个模块化扩散模型执行引擎,把"加载模型、文本编码、采样、解码"四步压成一张可以用 API 调用的节点图,SDXL、Flux 和超分模型能接在同一条链路里跑。矛盾在跑了一段时间后出现:节点一多,显存、模型换出和任务排队都成了瓶颈,任何一个没处理好都会拖垮吞吐。下面沿一条数据流,把流水线、调度和性能优化按数据流动的顺序讲完。

端到端流水线拆解:从 Checkpoint 到出图的五个节点

整体数据流如图所示:checkpoint 加载器产出 MODEL、CLIP、VAE 三条线,CLIP 产出条件张量 CONDITIONING,采样器在潜空间去噪,VAE 解码回像素域,最后落盘。

CheckpointLoaderSimple:权重进入流水线的唯一入口。职责是把一个 checkpoint 文件读进来,推断它的配置,拆成 MODEL/CLIP/VAE 三个输出。源码在 nodes.py。这里我们没用"diffusion_models、clip、vae 三目录分开加载"的方案:拆分方式灵活,但节点数翻倍,模型版本错配的概率也翻倍;单文件 checkpoint 自洽,运维成本最低。

def load_checkpoint(self, ckpt_name): ckpt_path = folder_paths.get_full_path_or_raise("checkpoints", ckpt_name) out = comfy.sd.load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, embedding_directory=...) return out[:3] # (MODEL, CLIP, VAE) # 上下文见 nodes.py:L634-L637

关键参数 ckpt_name 由 folder_paths.py 扫描 checkpoints 目录自动补全,不暴露绝对路径。数据从这里流出三条线:MODEL 进采样器,CLIP 进文本编码器,VAE 直通解码器。

CLIPTextEncode:把提示词变成条件张量。职责是把正负两路 prompt tokenize 后编码成 CONDITIONING。源码在 nodes.py。取舍上,编码走的是encode_from_tokens_scheduled而不是直接的 tokenize 加 encode:这个调度接口是 SDXL 双编码器、多编码器模型的挂接点,换模型时节点代码不用动。

def encode(self, clip, text): tokens = clip.tokenize(text) return (clip.encode_from_tokens_scheduled(tokens), ) # 上下文见 nodes.py:L73-L77

需要把长提示词分段加权时,不要改这个节点,用下游的 ConditioningCombine、ConditioningAverage 等 transform 节点拼。CONDITIONING 本质是列表,拼接节点是现成的。

KSampler:全流水线里唯一的计算大头。职责是把 latent 从纯噪声去噪到干净 latent,可调参数是 steps、cfg、sampler、scheduler 四件套。源码在 nodes.py。

def sample(self, model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise=1.0): return common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise=denoise) # 上下文见 nodes.py:L1622-L1623

参数取舍:steps 默认 20,我们压测里超过 25 基本看不到质量增益,纯属浪费时间;cfg 默认 8.0,叠 LoRA 后建议降到 5 到 6,避免纹理过饱和。denoise 是文生图和图生图的开关:1.0 是全新生成,低于 0.8 进入重绘模式。需要两段式去噪(先粗后细)就用同文件的 KSamplerAdvanced。

VAEDecode:回像素域的最后一跳。职责是把 latent 张量解码成像素图像。源码在 nodes.py。没有可调参数,关键处理是 batch 维度的合并:

def decode(self, vae, samples): latent = samples["samples"] images = vae.decode(latent) if len(images.shape) == 5: # Combine batches images = images.reshape(-1, images.shape[-3], images.shape[-2], images.shape[-1]) return (images, ) # 上下文见 nodes.py:L333-L340

SaveImage:输出的终止节点。职责是把图像写进 output 目录并推预览到前端。源码在 nodes.py。参数只有输出前缀 prefix 和压缩级别 compress_level(默认 4),没有取舍空间。它的价值是作为图的标准终止点:换成视频保存或上传节点时,上游全部不用动。这条流水线跑一次的产物大致如下:

SDXL 和 Flux 同时跑的时候,显存怎么分

具体场景:白天用 SDXL 出商品图,夜间用 Flux 做风格迁移,同一张 24GB 卡。两个任务交替排队时,最朴素的做法是模型常驻显存,这必然 OOM。ComfyUI 的做法是按需换出:新模型要加载时,系统盘点显存里已有的模型,按重要度排序,踢掉不重要的。

排序逻辑在 comfy/model_management.py:

can_unload.append((-shift_model.model_offloaded_memory(), sys.getrefcount(shift_model.model), shift_model.model_memory(), i)) can_unload_sorted = sorted(can_unload) # 上下文见 comfy/model_management.py:L863-L898

三个排序键依次是:已换出的显存量(取负号,优先保留已经在显存里的模型,因为踢出去再重新加载代价大)、引用计数(队列里还有未执行节点引用的模型不能踢)、模型总大小。同文件的load_models_gpu负责反向的加载,先算出所需显存再触发上面的换出流程。

模型热切换建立在这套机制之上:LoRA 是补丁不是独立模型,挂上摘下都不触发显存换出,实现见 comfy/lora.py,节点入口是 nodes.py 里的 LoraLoaderModelOnly。所以产线上的做法是"基础模型固定、LoRA 热切换",切换代价只有几百毫秒的矩阵运算,而不是重新加载权重。

要说边界:这套换出策略适合"多模型、串行或低并发"。如果业务要求两个视频大模型同时驻留并行出图,换出只会来回抖动,应该一开始就拆卡或拆机器,调度层没有魔法。

量化省下的显存怎么花:队列、量化、缓存的组合

三者各管一段成本,必须一起看:队列管"等待",量化管"装不装得下",缓存管"要不要重算"。

队列在 execution.py 中,每个任务带编号和优先级;API 侧从 server.py 的 /prompt 路由写入。压测环境(5 任务并发、SDXL 与 Flux 混跑)下,基线 P95 等待约 2 分钟,引入优先级队列和上面的显存换出后压到 40 秒左右,代价是要先定好优先级分级规则:交互调试 0、批量任务 5、可重试的低优任务 9,否则高优任务照样卡。

量化在 ComfyUI 里是一等公民,启动参数在 comfy/cli_args.py:

fpunet_group.add_argument("--fp8_e4m3fn-unet", action="store_true", help="Store unet weights in fp8_e4m3fn.") # 上下文见 comfy/cli_args.py:L93-L105,另有 --fp8_e4m3fn-text-enc

完整的量化格式(4bit 等布局、逐层混合精度)文档在 QUANTIZATION.md。我们最终选 fp8 而不是 4bit:4bit 省得更多,但压测里商品图的纹理细节肉眼可见地掉,客户投诉了;fp8 是放弃一半节省、质量可接受的折中。

缓存在 comfy_execution/caching.py 中,按节点输入参数生成缓存键,队列里两个任务经过同一节点且参数相同时,第二次直接复用第一次的结果。命中率对业务形态非常敏感:"同模型、100 条提示词"的创意批量任务命中率高,因为 checkpoint 加载和编码结构能复用;"每个任务参数都不同"的 A/B 实验命中接近零。

优化前后对比(单卡 4090 24GB,SDXL 1024x1024,5 任务并发压测):

指标基线优化后代价
显存稳态占用约 20GB约 14GB首帧加载慢约 2 秒(fp8 即时反量化)
单图生成耗时约 45 秒约 38 秒边缘纹理细节略降,需要质量验收
队列 P95 等待约 2 分钟约 40 秒必须定义优先级分级,否则高优任务仍会卡

这组数字的代价集中在"第一次请求"上:量化把部分存储成本转成了计算成本,首帧延迟略增;缓存只在参数重复时回本,且要额外接受缓存表的内存占用。所以上线顺序我们定为:先开队列(零成本),再上量化(质量需过验收),最后看命中率决定缓存的去留,低于一成直接关掉。

落地清单:硬件、监控与回滚

硬件最低配置(以 SDXL 1024x1024 文生图为基准):

组件最低推荐
GPURTX 4090 24GB 单卡A100 80GB,可常驻双大模型
内存32GB64GB
磁盘500GB SSDNVMe,模型目录单独分盘

从开发到生产的三个关键动作:

  1. 工作流 JSON 带版本号存入对象存储,代码只调队列 API,回滚对象永远是明确的版本。
  2. 用 extra_model_paths.yaml.example 配置共享模型目录,多实例不重复下载权重。
  3. 先用上面五个节点的最小链路验证产出正确,再加分支节点。

监控最低配置:只看两条曲线,显存占用和任务队列长度,日志走 api_server/services/terminal_service.py 或进程 stdout 即可。回滚方式:把 API 指向的工作流版本号回退一档,权重本身是只读的,不需要动。

自定义扩展入口:新节点照 custom_nodes/example_node.py.example 写,输入输出类型用 comfy/comfy_types/node_typing.py 声明。节点的参数选项都定义在 INPUT_TYPES 字典里,支持默认值、范围、惰性求值等修饰:

本方案适合多模型串行或低并发的图像产线;高并发视频生成请先拆机器。下一步:先跑通五节点最小链路,再叠 LoRA 和量化。

【免费下载链接】ComfyUIThe most powerful and modular diffusion model GUI, api and backend with a graph/nodes interface.项目地址: https://gitcode.com/GitHub_Trending/co/ComfyUI

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询