H3 LBH QKV Reuse (336 MiB)
336 MiB of cache that stops H3 projecting the same rows twice
- model
- MODEL
MiniMax H3's sparse attention has a structural inefficiency the community inherited rather than designed: to decide which rows to attend to, a "cold" attention call has to project its input to QKV once to compute statistics and sink rows, then walk the same input again to actually run sparse attention. The second walk re-runs the biggest linear layer in the block. This node makes the second walk a cache hit.
It's the fiddliest node in the pack, and the one most likely to throw at you - but when it works it's a straightforward win, which is why it ships on by default with the title-worthy cap of 336 MiB.
How it works
The node patches the model's sparse-attention path so that, for the duration of one attention call, block.attn.qkv_proj is wrapped in a bounded cache. The wrapper counts invocations, slices the work into 4096-row chunks, and stores each chunk's BF16 output keyed by the input tensor's storage pointer, shape, stride, dtype, device and version. On the second pass, if the key matches, the stored output comes back and the projection is skipped.
The scoping is the careful part, and it's what makes this safe rather than a memory leak. The cache is created per attention call, knows exactly how many projector invocations to expect, and releases itself when it hits that count - plus a mandatory release in a finally. There is no global monkeypatch and no cross-step retention. Below 8192 total rows it keeps every chunk; above that, only two of them.
Why 336 MiB specifically? Because one 4096-row chunk of 21504-wide BF16 output is about 168 MiB, so two chunks is ~336 MiB - the amount you can hold while keeping the high-resolution pass from spilling. It's a ceiling, not a promise: most of the time you'll use less, because the low-res pass is the one where everything fits.
Inputs and outputs
model in, MODEL out, plus two widgets: enabled (default true) and cache_mib (default 336, min 1, max 336 - the cap is enforced, not a suggestion). Lower cache_mib if you're tight on VRAM and you'll simply cache fewer chunks; the maths still happens, just not for free.
Place it after the core BlockSparseAttention node. It doesn't install sparse attention, it inspects what's already installed and wraps the block closures - which is why it validates so aggressively.
Install
Manager: search H3 LBH X2 Stream, install, restart. Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/sepiablue-ai/ComfyUI-H3-LBH-X2-Stream
then restart. Python side is thin (einops, safetensors); the hard requirements are ComfyUI features, not pip packages - native H3, SLA, dynamic VRAM, ConvRot W4A4 and compiled Sol-Attn. Models are manual: fused INT8 H3, Qwen3-VL NVFP4 encoder, INT8 video VAE, FP32 audio VAE, X2 Detail v1 VAE, LBH 3D FP16 upscaler. This node itself needs no model file.
Common issues
A wall of "QKV reuse requires…" errors. Each one names what's missing: SLA5 with exact audio rows, min_tokens4096/12288 and extra_tokens256 means your BlockSparseAttention settings drifted from the validated preset; "requires the 50-block MiniMax-H3 DiT" means wrong model; "requires native SLA as the current attention override" means the sparse node isn't upstream of this one, or something else replaced the attention override after it.
"Cannot bypass projection hooks or dynamic weight functions." Something is patching qkv_proj - a runtime LoRA or weight-patch node sitting on the attention projections. That's a genuine incompatibility: if the projection has hooks, skipping it to serve a cached tensor would silently drop the patch. Drop the hooking node or drop this one.
"QKV bounded reuse requires serial H3 attention calls." Two attention calls overlapped, which breaks the per-call accounting. It's a guard, not a crash to work around - remove whatever is running the attention concurrently.
Turning enabled off does not disable the validation. It only stops the caching; the wrapper, the environment checks and the sparse-attention contract checks all still run. If the node errors, toggling the widget won't rescue the graph - fix the wiring, or bypass/remove the node and accept the recompute.
Expect a modest gain, not a headline one. The pack's own claim for the whole pipeline is 20–25% faster on a 12GB 4070, and it's spread across F12 weights, QKV reuse, the 3+1 scheme and the X2 output stage. This node is one slice of that, and it costs you VRAM to get it. Keep it for the cap, drop it if you're chasing the last gigabyte.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| enabled | BOOLEAN | true | — |
| cache_mib | INT | 3361–336 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| MODEL | MODEL | — |