Nodes/ComfyUI-SyncReward/SyncReward Unload
ComfyUI Node

SyncReward Unload

Getting 10 GB of VRAM Back Without Restarting ComfyUI

By hiroki-abe-58·Created 2 days ago·Updated about 16 hours ago· 0
SyncReward Unload
    • report
    ◄after—►

    Here's the sequence that makes people install this node. You score a batch of clips, everything works, then you queue the next workflow - the one that upscales and re-renders - and it OOMs. Nothing changed in your prompt. What changed is that a 1.66 B-parameter model is sitting in VRAM in fp32, holding roughly 10 GB that ComfyUI's own memory manager doesn't know it can take back, because this pack manages its model slot itself.

    SyncReward Unload is the one-node fix. It's the least glamorous node in the pack and the one you'll end up adding to half your workflows.

    What it does

    Nothing complicated, and that's the appeal. It clears the resident SyncReward slot, runs a garbage collection, and asks torch to release its cached CUDA blocks. The model is gone - not swapped to system RAM, not marked for eviction, gone - and the next Score node that runs will pay the ten-second load again.

    Its single output is report, a JSON string. The node is marked as an output node, so it will always execute when it's in the graph, even though nothing downstream depends on it. That's deliberate: an unload that only runs when something consumes its output is an unload that silently doesn't run.

    The report looks like this:

    {
     "unloaded": true,
     "cuda_allocated_mib": 0
    }
    

    unloaded is whether there was actually a model resident to free. cuda_allocated_mib is torch's allocated figure right after the release, so you can watch it drop. If unloaded comes back false, either you never scored anything in this session or keep_loaded was off on the Loader and the model already left on its own. Neither is an error - but if you're chasing a VRAM problem, a false here tells you the memory is going somewhere else.

    The optional after input takes a FLOAT and is input-only - you can't type a number into it, you have to wire something in. That's not a limitation, it's the mechanism. ComfyUI will happily schedule branches in whatever order it likes, and an unload with no dependency can run before the scoring it's supposed to follow. Connecting the reward output from SyncReward Score (or best_reward from Select Best) into after forces the ordering: score first, unload second.

    If you skip after and your scores come back weird or the model reloads every single run, that's the first thing to check.

    When to use it instead of the Loader switch

    There are three ways to get the model out of VRAM, and they're not interchangeable.

    SyncReward Unload in the graph is the explicit one: fires after a specific point, tells you what it did. Use it when you want scoring to happen mid-workflow and the rest of the graph to get the GPU back.

    keep_loaded off on the Loader is the blunt one: the model is released after every job, no matter what. Great if SyncReward is a tip-and-tail step in a bigger pipeline, terrible if you're iterating on scores, because you'd reload ten seconds' worth of weights each time.

    ComfyUI's own "free model and node cache" button is the manual one, and it does work here - but it doesn't reach inside this pack's slot any more than this node reaches into ComfyUI's. They're separate memory managers. That's exactly why the node exists.

    Install

    cd ComfyUI/custom_nodes
    git clone https://github.com/hiroki-abe-58/ComfyUI-SyncReward
    python -m pip install -r ComfyUI-SyncReward/requirements.txt
    

    Or search "SyncReward" in ComfyUI Manager, then restart ComfyUI. Like Shift Audio, this node touches no weights - it just calls the release path inside syncreward_comfy/runtime.py. So if all you want is a way to evict a model, you can install the pack and skip tools/download_models.py entirely.

    Two things worth knowing

    Unloading doesn't rewind ComfyUI's view of the world: if an upstream SyncReward Loader is still in the graph, the next scoring node will quietly reload the weights. That's fine and by design, but it means the node solves a memory problem, not a workflow-ordering problem.

    And the release is best-effort on the torch side. gc.collect() plus empty_cache() frees what torch allocated through its caching allocator, which is all of this model - but if some other node in your graph is holding tensors, those don't evaporate. If your allocated figure doesn't drop, look at what else ran, not at this node.

    One last note on expectations: on a 5090 the load was measured at about 10 seconds and the peak while scoring at 9.8 GiB. On a card where that's a third of your VRAM, this unload is the difference between a workflow that fits and a workflow that becomes two workflows.

    Categoryloaders/syncreward

    Inputs (1)

    NameTypeDefaultDescription
    afteroptFLOATOptional: connect a reward so this runs after it

    Outputs (1)

    NameTypeDescription
    reportSTRING—