Fast Upscale Model - HearmemanAI
A fast drop-in replacement for ComfyUI's Upscale Image (using Model). Batches frames, blends tiles on the GPU, and defaults to no tiling.
Fast Upscale Model by HearmemanAI
A drop-in replacement for ComfyUI's Upscale Image (using Model). Same inputs, same upscale models, same output. It just stops doing three slow things.
Measured on an H200 with SPAN x2, 16 frames at 720p: 1.6s against stock's 15s, at 74.9 dB PSNR against stock's 58.4 dB when both are compared to an untiled reference. It comes out faster and closer to correct at the same time, because stock forces tiling even when the frame fits, and pays a quality cost for it.
Why the stock node is slow
None of it is the GPU's fault.
- It blends tiles on the CPU.
output_deviceisintermediate_device(), so every tile gets copied GPU to CPU, a 128-iteration Python loop builds a feather mask for each tile, and then the accumulate and divide run on a CPU thread. - It does one frame at a time.
for b in range(samples.shape[0]), so a 16-frame batch is 16 sequential passes. - It hardcodes
tile=512and only ever halves that on OOM, never raises it. A 720p frame that would fit in VRAM whole gets cut up anyway.
This node keeps the loop on the GPU, batches frames, builds each feather mask once per tile shape, and leaves tiling off by default.
Install
Through the ComfyUI Registry or Manager, or clone it:
cd ComfyUI/custom_nodes
git clone https://github.com/Hearmeman24/ComfyUI-HearmemanAI-Upscale
Nothing to install beyond what ComfyUI already ships. Restart, and the node shows up as HearmemanAI Upscale Model under the HearmemanAI category.
Wire it exactly like the stock node: Load Upscale Model into upscale_model, your images into image.
Settings
| input | default | what it does |
|---|---|---|
| precision | auto | auto means fp32 on CUDA. That is deliberate, see below. |
| batch_size | 8 | Frames per forward pass. VRAM scales linearly with it. The node backs off on its own if you OOM. |
| tile_size | 0 | 0 means no tiling, whole frame in one pass. Raise it only if a frame genuinely will not fit. |
| overlap | 32 | Only read when tile_size > 0. |
fp32 is the fast path on CUDA
Do not "optimize" this to fp16. I expected fp16 to win too, so I measured it instead of assuming.
On an H200 with SPAN x2, fp32 was the fastest and the most accurate setting. Every fp16 and bf16 variant came out slower. torch.backends.cudnn.allow_tf32 defaults to True, so fp32 convolutions are already running on tensor cores at TF32 rate. These upscaler architectures are small and bandwidth bound, so halving the bytes never earns back the cast overhead. The precision knob is there to buy VRAM headroom, not speed.
Tiling costs quality
Seam blending measured about 16 dB PSNR below an untiled pass. Stock tiles at 512 whether it needs to or not, and that is where most of its quality gap comes from.
Leave tile_size at 0 until you actually hit OOM. If you do, drop batch_size first. That is the order the automatic backoff already uses.
Correctness notes
The node calls the spandrel descriptor (upscale_model(x)), never the raw .model. The descriptor pads the input to satisfy the architecture's size_requirements and strips the padding back off afterwards. Skipping it is a tempting shortcut that quietly breaks any architecture that depends on it.
Tile blending is numerically equivalent to comfy.utils.tiled_scale, which test_upscale.py verifies with TF32 disabled. With TF32 on, the two differ by around 4e-6, because NHWC and NCHW cuDNN kernels accumulate in a different order. The test asserts a tight bound rather than bit-exactness for that reason.
License
MIT