π§ Offload CLIP to DRAM
The tiny node that lets a 20B text encoder share your GPU with the UNet
- clip
- trigger
- memory_stats
- dram_id
- passthrough
Qwen-Image-Edit is a 20B model, and the full-precision weights don't fit on a 24GB card together - the README's own numbers are a ~14.7GB CLIP plus a ~19.6GB UNet on a 3090 Ti. ComfyUI's automatic memory management shuffles things in and out, but it makes its own decisions. ArchAi3D_Offload_CLIP is the manual override: it says, out loud, "I'm done with the CLIP, shove it into CPU RAM so the UNet can have the VRAM."
What it is
A side-effect node. Its whole job is to move the CLIP text encoder's weights from VRAM to system RAM (DRAM) at a specific point in the graph, then pass your data through untouched. It's one of three memory nodes in the pack built around a shared DRAM cache system - its sibling ArchAi3D_Offload_Model does the same for the diffusion model itself.
The inputs and outputs
Two inputs:
clip(required) - the CLIP to offload, straight from your CLIP loader.trigger(optional, any type) - the trick. Connect the CONDITIONING output from your CLIPTextEncode here. The node executes because of the trigger, and your CONDITIONING flows through unchanged.
Three outputs:
memory_stats(STRING) - a live VRAM/RAM/cache status readout. Useful for confirming the offload actually happened.dram_id(STRING) - the cache key for this CLIP in DRAM, matching the key the pack's triggered loaders use, so the loader can find it again in ~1 second instead of re-reading from disk (~8 seconds).passthrough(any type) - your trigger input, unchanged. Connect it onward to the KSampler. This is the "Reroute but with a side effect" pattern: data flows through while the node does its thing.
How the mechanism works
The pack keeps a module-level cache in CPU RAM with strong references so nothing gets garbage-collected between runs. The offload node grabs the model's cache key, moves the weights to RAM, and the triggered loader checks that cache first on the next run. partially_unload() keeps the model tracked in ComfyUI's current_loaded_models, so if ComfyUI needs the CLIP again it auto-reloads from RAM instead of disk. The pipeline the README diagrams: encode text β offload CLIP β KSampler runs with the freed VRAM β decode. Everything shares one GPU, just at different times.
When to use it
If your workflow OOMs or you watch ComfyUI thrash while the CLIP and UNet fight for space, this is the fix. The pack's changelog flags two startup flags that make it behave: run ComfyUI with --normalvram --cache-classic. It's not a replacement for ComfyUI's memory management - it's an explicit, manual system for people who want to say exactly when each model is on GPU. If you don't know why you'd need it, you don't need it yet.
Install
It ships with the pack - ComfyUI Manager ("ArchAi3d Qwen") or:
cd ComfyUI/custom_nodes
git clone https://github.com/amir84ferdos/ComfyUI-ArchAi3d-Qwen.git
cd ComfyUI-ArchAi3d-Qwen && pip install -r requirements.txt
Restart, then it's under ArchAi3d/Memory. No model downloads beyond whatever Qwen Edit workflow you're already running.
Gotchas
- The trigger input is the order-control. Without it, ComfyUI may execute the offload before the CLIP is used, and you'll offload nothing. Always wire CLIPTextEncode's CONDITIONING through the trigger.
- Watch your system RAM. The cache holds real weights in RAM; on a 16GB-RAM box a 20B model in DRAM plus the OS can get tight. The pack's DRAM guide covers Linux swap settings (
vm.swappiness) for exactly this reason. - This is explicit, not automatic. It only frees what you tell it to free, at the point you tell it to. That's the feature.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| clip | CLIP | CLIP text encoder to offload from VRAM to DRAM | |
| triggeropt | * | Connect CLIPTextEncode CONDITIONING output here β it passes through to 'passthrough' output |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| memory_stats | STRING | Current VRAM/RAM/cache status |
| dram_id | STRING | Cache key for this CLIP in DRAM (matches loader's key) |
| passthrough | * | Pass-through of trigger input (e.g. CONDITIONING β connect to KSampler) |