v2.4.3 - HSWQ Purge — NVFP4 Runtime Pool Clear (Method 2c)
| EN | 中文 |
1. Error content
1.1 Observed sequence
- First prompt with SDXL HSWQ NVFP4 (ConvRot / kitchen
quantize_nvfp4path): succeeds. - DistOrch Purge VRAM V2 runs with the HSWQ toggle enabled (log prefix historically
HSWQ INT8:). - Second prompt in the same ComfyUI process: forward floods with kitchen / TC pooled path failures.
1.2 Representative exception (second run)
pooled TC path failed: quantize_nvfp4() ... Invoked with types: PyCapsule ...
Typical shape:
- Exception class:
TypeError(or wrapper loggingpooled TC path failed) - Kernel / binding:
comfy_kitchenCUDA extensionquantize_nvfp4 - Unexpected argument type:
PyCapsule(DLPack / dead tensor storage handle) where a live CUDA tensor buffer was expected - Timing: after DistOrch HSWQ purge methods (especially nuclear CUDA tensor kill + Method 2c kitchen reset that previously only cleared kitchen dicts)
1.3 What the first-run log looked like (healthy)
On a healthy first run you typically see:
- NVFP4 stack applied (
full stack applied/ LoRA bake OK) - No
pooled TC path failed - No
PyCapsuleinquantize_nvfp4argument types
1.4 What DistOrch logged before this fix (relevant excerpt)
Method 2c ran, but only kitchen-side bags were reported, for example:
HSWQ INT8: Method 2c - Reset comfy_kitchen CUDA caches...
HSWQ INT8: Reset comfy_kitchen CUDA caches _cublas_workspaces=..., _empty_cuda_tensors=...
Missing (before the fix): any line such as:
Cleared HSWQ NVFP4 runtime pools / CUDA graphs
... nvfp4_runtime_pools ...
That absence is the DistOrch-side smoking gun: kitchen caches were reset; NVFP4 module-level pools were not.
1.5 Related INT8 historical error (same Method 2c family)
INT8 second-gen after purge previously failed with a similar pattern on kitchen cuBLAS workspace:
cublas_gemm_int8 ... PyCapsule ...
Method 2c already cleared _cublas_workspaces / _empty_cuda_tensors for that INT8 case. The NVFP4 failure is the same structural class on a different pool owner (nvfp4_runtime).
2. Root cause
2.1 DistOrch HSWQ purge destroys CUDA storage aggressively
When the HSWQ purge toggle is on, DistOrch walks several methods, including:
| Method | Role |
|---|---|
| Method 0 / 0s | PinCache / Detailer SEGS / PromptExecutor cache |
| Method 1 | unload_all_models / free_memory |
| Method 2 / 2b | Force-unregister ComfyUI PINNED_MEMORY |
| Method 3 | Nuclear gc scan — replace / kill large CUDA tensor storage |
| Method 2c | Reset module-level CUDA caches that still hold dead refs |
Method 0s / Method 3 do not only free “models”. They walk live Python objects (gc.get_objects() / SEGS sweeps) and replace or empty CUDA storage on large tensors still referenced from caches.
2.2 HSWQ NVFP4 keeps module-level pools outside those dicts
In ComfyUI-nunchaku-unofficial-loader, NVFP4 activation / rotation / CUDA-graph reuse lives in:
nodes/nvfp4/nvfp4_runtime.py
Conceptually:
| Symbol | Role |
|---|---|
_ACT_Q_POOL |
Reused qx / scale buffers for quantize_nvfp4_act_pooled |
_ROT_OUT_POOL |
Reused rotate matmul outputs |
_GRAPH_CACHE |
CUDA graphs for NVFP4 GEMM paths |
clear_nvfp4_runtime_pools() |
Clears all of the above |
Those pools are module globals. DistOrch Method 3 may:
- Find the tensors inside the pools via
gc, and - Destroy / replace their CUDA storage,
while the dict entries remain, still pointing at Python tensor objects whose backing storage is now invalid.
2.3 Why reload alone does not heal it
After purge:
- UNet / MODEL may be unloaded and later reloaded.
- Reloading weights does not recreate
_ACT_Q_POOL/_ROT_OUT_POOL/_GRAPH_CACHE. - The next forward still calls
quantize_nvfp4_act_pooled→ kitchen_C.quantize_nvfp4(...). - Kitchen wraps buffers for DLPack; dead storage surfaces as
PyCapsulein the binding →TypeError.
2.4 Why INT8 clear was already present but NVFP4 still broke
Method 2c already cleared comfy_kitchen module dicts:
_cublas_workspaces_empty_cuda_tensors
That is sufficient for the INT8 cublas_gemm_int8 PyCapsule class.
NVFP4 pools live in another package (nvfp4_runtime), not in comfy_kitchen.backends.cuda. Clearing kitchen alone left NVFP4 pools poisoned.
2.5 Secondary import trap (why an install can look “unfixed”)
This repository ships two purge implementations:
| Path | Role |
|---|---|
nodes/purge_vram.py |
Canonical implementation (Method 2c + NVFP4 clear) |
root purge_vram.py |
Legacy copy; may lag behind nodes/ |
If __init__.py imported root first, ComfyUI kept executing the old Method 2c (kitchen only). Preferring .nodes.purge_vram is part of making the DistOrch-side NVFP4 clear actually run after users update the custom node.
3. Countermeasure overview
3.1 DistOrch-side strategy (this guide)
- Keep existing INT8 / kitchen Method 2c behavior.
- Add a call to
clear_nvfp4_runtime_pools()from any loadednvfp4_runtimemodule found insys.modules(the import name varies across ComfyUI custom-node layouts). - Prefer importing
DisTorchPurgeVRAMV2from.nodes.purge_vramso an outdated rootpurge_vram.pycannot silently win. - Retarget log prefix from
HSWQ INT8:toHSWQ INT8/NVFP4:so purge logs match the dual INT8+NVFP4 Method 2c scope.
3.2 What success looks like in DistOrch logs
After ComfyUI restart with the fixed nodes/purge_vram.py loaded:
HSWQ INT8/NVFP4: Method 2c - Reset comfy_kitchen CUDA caches...
HSWQ INT8/NVFP4: Cleared HSWQ NVFP4 runtime pools / CUDA graphs
HSWQ INT8/NVFP4: Reset comfy_kitchen CUDA caches ..., nvfp4_runtime_pools
(Exact kitchen bag names may vary; the important new token is nvfp4_runtime_pools and/or the “Cleared HSWQ NVFP4 runtime pools” line.)
Then: first prompt → purge → second prompt must show zero pooled TC path failed / PyCapsule on quantize_nvfp4.
3.3 What this DistOrch fix does not claim
- It does not replace loader-side early-return / upgrade pool clears (those are a separate safety net in unofficial-loader).
- It does not delete Nunchaku SVDQ paths.
- It does not change INT8 PinCache / SEGS / Method 3 algorithms beyond log labeling and the Method 2c NVFP4 addition.
3.4 Where the fix lives in this repository
| File | Role for this fix |
|---|---|
nodes/purge_vram.py |
Canonical Method 2c body (kitchen + NVFP4 pool clear) |
__init__.py |
Imports DisTorchPurgeVRAMV2 from .nodes.purge_vram first |
purge_vram.py (repository root) |
Legacy fallback only; may not include NVFP4 clear |
Users obtain the fix by updating ComfyUI-DistorchMemoryManager from this repository (Manager / git pull / release package), then fully restarting ComfyUI so the new nodes/purge_vram.py is loaded.
4. Modified file names
4.1 DistOrch (required for this fix)
| File | Change |
|---|---|
nodes/purge_vram.py |
Extended _reset_comfy_kitchen_cuda_caches() to clear HSWQ NVFP4 runtime pools; Method 2c call site unchanged in role; log prefix HSWQ INT8/NVFP4: |
__init__.py |
Prefer from .nodes.purge_vram import DisTorchPurgeVRAMV2; root purge_vram is fallback only |
4.2 Paths inside this repository
nodes/purge_vram.py__init__.py
On GitHub:
- https://github.com/ussoewwin/ComfyUI-DistorchMemoryManager/blob/main/nodes/purge_vram.py
- https://github.com/ussoewwin/ComfyUI-DistorchMemoryManager/blob/main/__init__.py
4.3 External API consumed (not a DistOrch file; called by DistOrch)
Loader function DistOrch invokes when the module is loaded:
- Package:
ComfyUI-nunchaku-unofficial-loader - Module:
nodes.nvfp4.nvfp4_runtime - Function:
clear_nvfp4_runtime_pools()
Minimal definition (loader side, for reference):
def clear_nvfp4_runtime_pools() -> None:
_ACT_Q_POOL.clear()
_ROT_OUT_POOL.clear()
clear_nvfp4_cudagraphs()5. Full text of the added / modified DistOrch code
5.1 __init__.py — Purge VRAM V2 import preference
# Import Purge VRAM V2 node.
# Prefer nodes.purge_vram (Method 2c clears HSWQ NVFP4 runtime pools after DistOrch
# nuclear kill). Root purge_vram.py is legacy fallback only.
try:
from .nodes.purge_vram import DisTorchPurgeVRAMV2
print("[ComfyUI-VRAM-Manager] Successfully imported DisTorchPurgeVRAMV2 from .nodes.purge_vram")
except ImportError as e:
try:
from .purge_vram import DisTorchPurgeVRAMV2
print("[ComfyUI-VRAM-Manager] Successfully imported DisTorchPurgeVRAMV2 from .purge_vram")
except ImportError as e2:
print(f"[ComfyUI-VRAM-Manager] WARNING: Failed to import DisTorchPurgeVRAMV2: {e2}")
DisTorchPurgeVRAMV2 = None5.2 nodes/purge_vram.py — _reset_comfy_kitchen_cuda_caches (Method 2c body)
This is the complete nested function as shipped for INT8 kitchen + NVFP4 pool clear (log prefix included):
def _reset_comfy_kitchen_cuda_caches() -> None:
"""Drop comfy_kitchen + HSWQ NVFP4 pooled CUDA buffers after nuclear kill.
Method 0s / Method 3 walk gc.get_objects() and replace storage on
large CUDA tensors. That includes:
- comfy_kitchen cuBLAS workspace (``_cublas_workspaces``) →
dead tensor → ``cublas_gemm_int8`` PyCapsule (INT8 2nd gen)
- HSWQ NVFP4 ``_ACT_Q_POOL`` / ``_ROT_OUT_POOL`` / CUDA-graph
cache → dead qx/sx buffers → ``quantize_nvfp4`` PyCapsule
(ConvRot NVFP4 2nd gen after DistOrch purge)
Model reload alone does not recreate those module-level pools.
"""
try:
import comfy_kitchen.backends.cuda as ck_cuda
except Exception as e:
print(f"HSWQ INT8/NVFP4: comfy_kitchen cuda import skipped: {e}")
ck_cuda = None
cleared = []
if ck_cuda is not None:
for attr in (
"_cublas_workspaces",
"_empty_cuda_tensors",
):
bag = getattr(ck_cuda, attr, None)
if isinstance(bag, dict) and bag:
n = len(bag)
bag.clear()
cleared.append(f"{attr}={n}")
# HSWQ NVFP4 act / GEMM pools — scan sys.modules (import path varies).
for name, mod in list(__import__("sys").modules.items()):
if mod is None:
continue
if not (
name.endswith("nvfp4_runtime")
or ".nvfp4_runtime" in name
):
continue
fn = getattr(mod, "clear_nvfp4_runtime_pools", None)
if not callable(fn):
continue
try:
fn()
cleared.append("nvfp4_runtime_pools")
print(
"HSWQ INT8/NVFP4: Cleared HSWQ NVFP4 runtime pools / CUDA graphs"
)
break
except Exception as e2:
print(
f"HSWQ INT8/NVFP4: NVFP4 runtime pool clear failed ({name}): {e2}"
)
if cleared:
print(
"HSWQ INT8/NVFP4: Reset comfy_kitchen CUDA caches "
+ ", ".join(cleared)
)
else:
print("HSWQ INT8/NVFP4: comfy_kitchen CUDA caches already empty")5.3 nodes/purge_vram.py — Method 2c call site (HSWQ purge sequence)
# Nuclear CUDA tensor kill may have destroyed kitchen workspaces
# while leaving dead refs in module-level dicts — clear them.
print("HSWQ INT8/NVFP4: Method 2c - Reset comfy_kitchen CUDA caches...")
_reset_comfy_kitchen_cuda_caches()5.4 Log prefix change (same file)
All HSWQ purge print lines that previously used the prefix HSWQ INT8: now use:
HSWQ INT8/NVFP4:
Rationale: Method 2c (and the HSWQ purge toggle) cover both INT8 kitchen workspace clear and NVFP4 runtime pool clear. A log that says only INT8 is misleading when NVFP4 pools are also cleared on the same path.
6. Meaning of the code
6.1 Why Method 2c exists at all
Methods 0s / 3 are storage killers. They are correct for freeing VRAM held by SEGS / executor caches / stray CUDA tensors. They are incorrect if module-level dicts keep names pointing at those tensors after storage death.
Method 2c is the dictionary hygiene step:
- Clear kitchen workspaces that INT8 GEMM reuses.
- Clear NVFP4 pools/graphs that NVFP4 act quant / GEMM reuses.
Without Method 2c, “purge succeeded” (allocated VRAM drops) can still leave poisoned caches for the next forward.
6.2 Kitchen clear block (_cublas_workspaces, _empty_cuda_tensors)
for attr in ("_cublas_workspaces", "_empty_cuda_tensors"):
bag = getattr(ck_cuda, attr, None)
if isinstance(bag, dict) and bag:
bag.clear()Meaning:
- These dicts cache CUDA tensors for comfy_kitchen performance.
- After nuclear kill, entries can be dead.
.clear()drops the references so the next INT8 kitchen call allocates fresh buffers.- This is the historical INT8 second-gen fix; kept unchanged in role.
6.3 NVFP4 sys.modules scan
for name, mod in list(__import__("sys").modules.items()):
if name.endswith("nvfp4_runtime") or ".nvfp4_runtime" in name:
fn = getattr(mod, "clear_nvfp4_runtime_pools", None)
if callable(fn):
fn()
...
breakMeaning:
- DistOrch must not hard-depend on one frozen import string (
nodes.nvfp4.nvfp4_runtimevs other package-qualified names ComfyUI may register). - Scanning
sys.modulesfinds whatever name ComfyUI actually used when the unofficial-loader was imported. - Calling
clear_nvfp4_runtime_pools()empties_ACT_Q_POOL,_ROT_OUT_POOL, and CUDA graphs. breakafter first success: one clear is enough; avoid double-clear noise.
If the loader was never imported this session: no matching module → no nvfp4_runtime_pools in the cleared list. That is correct (nothing to clear). After an NVFP4 run, the module should be present.
6.4 Why DistOrch calls the loader API instead of duplicating pools
Pools are owned by unofficial-loader. DistOrch should not reimplement pool keys. The contract is:
- Loader exports
clear_nvfp4_runtime_pools(). - DistOrch Method 2c calls it after nuclear kill.
- Next NVFP4 forward rebuilds pools on demand with live storage.
6.5 __init__.py import preference — meaning
from .nodes.purge_vram import DisTorchPurgeVRAMV2 # first
# root purge_vram only if nodes import failsMeaning:
- Operational truth is
nodes/purge_vram.py. - Root
purge_vram.pycan drift (older Method 2c without NVFP4 clear, line-ending churn, partial updates). - Preferring nodes makes “Method 2c was updated in
nodes/” equal “ComfyUI actually runs that Method 2c”. - Startup log must show:
Successfully imported DisTorchPurgeVRAMV2 from .nodes.purge_vram
If it shows .purge_vram (root), the install is on the legacy import path — NVFP4 clear may be missing until nodes/purge_vram.py imports successfully.
6.6 Log prefix HSWQ INT8/NVFP4: — meaning
- Toggle UI may still be labeled
HSWQ(legacy workflows acceptHSWQ INT8). - Console prefix documents that this purge path handles INT8 kitchen + NVFP4 pools.
- Operators grepping logs for second-gen bugs should search
HSWQ INT8/NVFP4andnvfp4_runtime_pools.
6.7 Causal chain (compact)
Run 1 NVFP4
→ fills _ACT_Q_POOL / graphs with live CUDA tensors
DistOrch HSWQ purge Method 0s/3
→ destroys CUDA storage of those tensors (VRAM freed)
Method 2c kitchen-only (old)
→ kitchen dicts cleared; NVFP4 pools STILL hold dead tensors
Run 2 NVFP4
→ quantize_nvfp4 sees PyCapsule / TypeError
Method 2c + clear_nvfp4_runtime_pools (new)
→ pools empty; Run 2 allocates fresh buffers → OK
6.8 Verification checklist
- Full ComfyUI restart after updating ComfyUI-DistorchMemoryManager to a build that includes this Method 2c change.
- Startup: import from
.nodes.purge_vram. - Run NVFP4 once.
- Run DistOrch HSWQ purge; confirm Method 2c logs
nvfp4_runtime_pools/ “Cleared HSWQ NVFP4…”. - Second NVFP4 prompt: no
pooled TC path failed, noPyCapsuleonquantize_nvfp4.
Appendix A — Relation to unofficial-loader (out of DistOrch scope)
Loader-side pool clear on early-return / stack upgrade is a separate safety net for paths that never run DistOrch Method 2c. For the reported failure (purge then second prompt), DistOrch Method 2c NVFP4 clear is the primary fix.
Appendix B — Document map
| Section | This document |
|---|---|
| (1) Error content | §1 |
| (2) Root cause | §2 |
| (3) Countermeasure overview | §3 |
| (4) Modified file names | §4 |
| (5) Full modified code | §5 |
| (6) Meaning | §6 |
End of DistOrch NVFP4 Method 2c guide.