Nodes/ComfyUI-SaveMem/SaveLatentsMemory
ComfyUI Node

SaveLatentsMemory

Hand the raw latent to your own code

By hnmr293·Created about a year ago·Updated about a year ago· 1
SaveLatentsMemory
  • latents
    name
    dummy_input0

    Same pack, same idea as SaveImagesMemory, one crucial difference: where its sibling writes a finished PNG, this node writes the raw latent tensor that came out of your sampler. Still compressed, still in latent space, never decoded to pixels. That's the whole point - if whatever sits on the other end of your pipeline is more neural net (another ComfyUI instance, your own sampler loop, a script that wants to keep sampling instead of look at a picture), the VAE decode is exactly the step you don't need. Why burn a decode, ship a PNG, and encode it again on the other side when you can just pass the tensor?

    It's another niche API/interop tool from hnmr293 (the sd-webui-cutoff and ComfyUI-nodes-hnmr developer), and the README doesn't pretend otherwise: intended for use via API. If you're a UI-clicking workflow person, this is not for you, and it definitely doesn't "save memory" in the VRAM sense - that name trap is worth catching now.

    What it actually does

    The node attaches to a named multiprocessing.shared_memory.SharedMemory segment that your receiver process created in advance, then torch.saves the latent's samples tensor - moved to CPU first - into it. The layout is dead simple:

    • 8 bytes: the length of the data that follows (native endian)
    • the rest: the torch.save bytes

    To get the tensor back on the other side you torch.load those bytes. One honest caveat from the source: only the samples tensor is written. ComfyUI stashes a noise_mask alongside latents in inpainting workflows, and that does not make the trip - so don't use this to ship masked latents around.

    The inputs

    Three, same shape as the sibling node:

    • name (STRING) - the shared memory tag. Must exactly match the segment your receiver created.
    • latents (LATENT) - wire from a sampler.
    • dummy_input (INT, optional) - the rerun trigger, because a node with no output can otherwise get cached and skipped on re-runs.

    No outputs; it's a terminal node. It runs, writes, returns an empty dict.

    Installing it

    Identical to the pack's other node - ComfyUI Manager → Install Custom Nodes → search ComfyUI-SaveMem, restart. Or:

    cd ComfyUI/custom_nodes
    git clone https://github.com/hnmr293/comfyui-savemem
    

    Restart and you're done. No extra pip packages (torch, numpy, PIL are all already in ComfyUI), no models to download, MIT-licensed.

    Common issues

    • FileNotFoundError - the segment doesn't exist yet. Create it on the receiver side first, per the README.
    • "Buffer size X is smaller than required Y" - latents are bigger than people expect. An SDXL latent at 1024² is 4×128×128 channels of fp32, so on the order of 16 MB before you even add pickling overhead. Size your segment accordingly, or you'll get a runtime error and a half-written header.
    • Stale reads on re-run - bump dummy_input.
    • The pickle caveat - torch.load executes pickled code. Only load segments you created yourself; don't accept shared memory from something you don't trust. And since shared memory is per-machine, the reader has to be on the same box.

    A receiver that hands the tensor back to ComfyUI-style dicts:

    import io, struct, torch
    from multiprocessing import shared_memory as sm
    
    shim = sm.SharedMemory(name="my_latent")  # you created it earlier
    length, = struct.unpack("=Q", shim.buf[:8])
    samples = torch.load(io.BytesIO(bytes(shim.buf[8:8+length])), map_location="cpu")
    shim.close()
    latent = {"samples": samples}  # feed straight into a Decode node, or keep sampling
    

    It's a tool you reach for exactly once you've built the other half of the pipe. When that day comes, it's refreshingly small and honest about what it does.

    Categoryhnmr/latent

    Inputs (3)

    NameTypeDefaultDescription
    nameSTRINGname of the shared memory
    latentsLATENT
    dummy_inputoptINT0dummy input for rerunning

    Outputs (0)

    No outputs