[GH-ISSUE #188] [gfx1201] hip backend missing bare (non-_dtype) dequant functions — silently falls back to slow single-threaded eager backend during model load #56

Open
opened 2026-10-26 22:19:47 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @CS6 on GitHub (Sep 20, 2026).
Original GitHub issue: https://github.com/Comfy-Org/comfy-kitchen/issues/188

Summary

On an AMD Radeon AI PRO R9700 (RDNA4, gfx1201), loading any quantized model whose weights go through comfy/ops.py's _load_quantized_module() path takes 10–60+ minutes for models in the tens-of-GB range, pegging a single CPU core at ~95-100% the whole time while GPU utilization stays near 0%.

Root cause: comfy_kitchen's hip backend implements the _dtype-suffixed variant of several dequantization functions, but not the bare (no-suffix) variant that comfy/ops.py actually calls during weight loading. Since BackendRegistry's priority order is ["hip", "cuda", "triton", "eager"] and hip doesn't advertise the bare function name as a capability, dispatch silently falls through all the way to eager — a pure-Python/generic-PyTorch implementation with no ROCm acceleration — for every single weight tensor.

This reproduces on a completely clean installation (official rocm/pytorch:rocm10.0_ubuntu26.04_py3.14_pytorch_release_2.13.0 Docker image, comfy-kitchen==0.2.35, amd-torch-device-gfx1201==2.13.0+rocm10.0.0 installed alongside torch as required), so it is not an environment misconfiguration.

Environment

  • GPU: AMD Radeon AI PRO R9700 (RDNA4, gfx1201)
  • OS/Image: rocm/pytorch:rocm10.0_ubuntu26.04_py3.14_pytorch_release_2.13.0 (official AMD image)
  • torch: 2.13.0+rocm10.0.0 (+ amd-torch-device-gfx1201==2.13.0+rocm10.0.0 installed alongside, required separately for any kernel to dispatch at all — see note below)
  • comfy-kitchen: 0.2.35 (also reproduced on 0.2.10)
  • ComfyUI: current master as of 2026-09-20
  • Custom nodes involved: ComfyUI_MinimaxH3HybridLoader / ComfyUI-MiniMax-H3-Turbo (MiniMax H3 video model, int8_convrot-quantized ~25GB text encoder + ~20GB diffusion transformer), but the same "Found quantization metadata version 1" / "Using MixedPrecisionOps for text encoder" hang has also been independently reported by other users loading unrelated models (e.g. Pixal3D/Trellis2) on the same gfx1201 architecture — see "Related reports" below. So this does not appear to be model-specific.

Steps to reproduce

  1. On a gfx1201 machine, install a clean comfy-kitchen==0.2.35 + matching ROCm-10.0 torch stack (torch, amd-torch-device-gfx1201, rocm-sdk-core/rocm-sdk-libraries/rocm-sdk-device-gfx1201, all ==...+rocm10.0.0/10.0.0).
  2. Verify basic ops work (torch.ones(4,4,device='cuda')+1, fp16 matmul) — they do, confirming the base install is sound.
  3. Load any model whose weights use comfy.ops.MixedPrecisionOps with int8_convrot (or plain int8) quantization and a non-trivial weight count (hundreds to low thousands of tensors).
  4. Observe the log hang at:
    Found quantization metadata version 1
    Using MixedPrecisionOps for text encoder
    CLIP/text encoder model load device: cuda:0, offload device: cpu, current: cpu, dtype: torch.float16
    Requested to load MiniMaxH3TEModel_
    Model MiniMaxH3TEModel_ prepared for dynamic VRAM loading. 25882MB Staged. 0 patches attached. Force pre-loaded 310 weights: 1272 KB.
    
  5. ps -o pid,etimes,time,pcpu on the ComfyUI process shows a single thread pinned at ~90-100% CPU for the entire duration; rocm-smi --showuse shows ~0-3% GPU utilization the whole time.

Root cause

>>> import comfy_kitchen
>>> from comfy_kitchen.registry import registry
>>> registry._priority
['hip', 'cuda', 'triton', 'eager']
>>> sorted(registry._capabilities['eager'] - registry._capabilities['hip'])
['dequantize_int8_convrot_weight', 'dequantize_int8_embedding', 'dequantize_int8_simple',
 'dequantize_mxfp8', 'dequantize_nvfp4', 'prepare_int4_weight_for_int8_linear',
 'quantize_mxfp8', 'quantize_nvfp4', 'rotate_int8_convrot_weight',
 'scaled_mm_mxfp8', 'scaled_mm_nvfp4']

comfy_kitchen/backends/hip/__init__.py defines dequantize_int8_convrot_weight_dtype(...) and dequantize_int8_simple_dtype(...), but never registers the bare dequantize_int8_convrot_weight / dequantize_int8_simple names that comfy/ops.py's model-loading path (_load_quantized_module → weight dequant/requant) actually calls. comfy_kitchen's own cuda backend does implement both bare functions as one-line wrappers around the _dtype version with a float32 default:

# backends/cuda/__init__.py
def dequantize_int8_convrot_weight(q, scale, group_size) -> torch.Tensor:
    return dequantize_int8_convrot_weight_dtype(q, scale, group_size, DTYPE_TO_CODE[torch.float32])

The hip backend has no equivalent — the bare name is simply absent from its _build_constraints() capability dict — so dispatch has no hip candidate for that function name and falls through the priority list to eager.

Confirmed fix (partial)

Adding the same one-line wrapper pattern to comfy_kitchen/backends/hip/__init__.py for both functions, and adding the corresponding entries to _build_constraints(), does work — verified directly:

>>> import comfy_kitchen
>>> from comfy_kitchen.registry import registry
>>> 'dequantize_int8_convrot_weight' in registry._capabilities['hip']
True
>>> comfy_kitchen.backends.hip.dequantize_int8_convrot_weight(q, scale, 64)  # runs on GPU, correct output
# added to backends/hip/__init__.py, right after dequantize_int8_simple_dtype:
def dequantize_int8_simple(q: torch.Tensor, scale: torch.Tensor) -> torch.Tensor:
    return dequantize_int8_simple_dtype(q, scale, DTYPE_TO_CODE[torch.float32])

# added right after dequantize_int8_convrot_weight_dtype:
def dequantize_int8_convrot_weight(q: torch.Tensor, scale: torch.Tensor, group_size: int) -> torch.Tensor:
    return dequantize_int8_convrot_weight_dtype(q, scale, group_size, DTYPE_TO_CODE[torch.float32])

(plus matching "dequantize_int8_simple" / "dequantize_int8_convrot_weight" entries in _build_constraints(), copied from the _dtype entries minus the output_dtype_code param.)

However, this only partially fixes the hang. After applying it, the exact same model still hangs at the exact same log line for 30+ minutes. dequantize_int8_embedding has no hip implementation at all (not even a _dtype variant to wrap) — this is almost certainly the actual remaining bottleneck for text-encoder loading specifically, since text encoders have large embedding tables and this function has no accelerated path whatsoever on hip. That one needs an actual native implementation, not a wrapper — beyond what I can contribute myself.

Note: a second, unrelated blocker on the way to reproducing this

For anyone trying to reproduce: on a from-scratch ROCm 10.0 install, torch.cuda.is_available() returns True and the device name resolves correctly, but every kernel launch (including torch.ones(4,4,device='cuda')+1) fails with hipErrorInvalidImage unless amd-torch-device-gfx1201==2.13.0+rocm10.0.0 is also installed alongside torch/rocm-sdk-device-gfx1201 (it's a separate package carrying torch's external "kernel pack" for the exact arch — not obviously implied by the official install docs). Not a comfy-kitchen bug, just a prerequisite worth knowing before debugging anything else on this chip.

  • Comfy-Org/ComfyUI#15001 — same architecture (R9700/gfx1201), same "Found quantization metadata version 1" hang signature, different model (LTX 2.3). Root-caused there to mmap-backed .to(device) overhead (~0.5s/tensor fixed cost) rather than this dispatch gap — likely a second, independent slow-path on this architecture, not the same bug, since --disable-mmap did not resolve the hang reported here.
  • r/comfyui thread on running Pixal3D/Trellis2 via comfy-kitchen's hip backend: works fine in 93.99s on RDNA3 (7900XT, gfx1100), but hangs for 3+ minutes on RDNA4 (RX 9070, gfx1201) in the same _naf_hr code path, with per-step timings showing the RDNA4-specific slowdown isolated to specific tensor-shape calls. Same generational pattern: RDNA3 fine, RDNA4 (gfx1201) broken.
  • ROCm/TheRock#7992, #7861, #7839 — separate but related gfx1201/gfx1151 kernel-dispatch regressions in the ROCm 10.0/torch 2.13 "kernel pack" architecture, 7.14.0 reported as the last known-good build across multiple of these.

Ask

  • Add the missing bare dequantize_int8_convrot_weight / dequantize_int8_simple wrappers to the hip backend (patch above, happy to open a PR if useful).
  • A native (or wrapped) hip implementation of dequantize_int8_embedding would likely be needed to fully resolve model-loading hangs for models with quantized embedding tables (e.g. text encoders) on gfx1201.
  • More generally: is there a way to make dispatch gaps like this visible (e.g. a warning log the first time a function falls through to eager for a "should be accelerated" backend), rather than silent? This would have saved a lot of debugging time — the log gives no indication that a 25GB model is about to be processed through a generic Python fallback path.

Note

I see #183 (open, not yet merged) is already tuning the HIP INT8 ConvRot compute kernel specifically for gfx1200/gfx1201 with H3-shaped projections (@qweqweewqe7-create) — that's a different layer of the same code path (kernel geometry/perf, once dispatch actually reaches hip) rather than this dispatch-gap issue, but tagging for visibility since it's clearly the same problem area and hardware. Would be great to get these two efforts pointed at each other.

Originally created by @CS6 on GitHub (Sep 20, 2026). Original GitHub issue: https://github.com/Comfy-Org/comfy-kitchen/issues/188 ## Summary On an AMD Radeon AI PRO R9700 (RDNA4, `gfx1201`), loading any quantized model whose weights go through `comfy/ops.py`'s `_load_quantized_module()` path takes **10–60+ minutes** for models in the tens-of-GB range, pegging a single CPU core at ~95-100% the whole time while GPU utilization stays near 0%. Root cause: `comfy_kitchen`'s `hip` backend implements the `_dtype`-suffixed variant of several dequantization functions, but not the bare (no-suffix) variant that `comfy/ops.py` actually calls during weight loading. Since `BackendRegistry`'s priority order is `["hip", "cuda", "triton", "eager"]` and `hip` doesn't advertise the bare function name as a capability, dispatch silently falls through all the way to `eager` — a pure-Python/generic-PyTorch implementation with no ROCm acceleration — for every single weight tensor. This reproduces on a completely clean installation (official `rocm/pytorch:rocm10.0_ubuntu26.04_py3.14_pytorch_release_2.13.0` Docker image, `comfy-kitchen==0.2.35`, `amd-torch-device-gfx1201==2.13.0+rocm10.0.0` installed alongside torch as required), so it is not an environment misconfiguration. ## Environment - GPU: AMD Radeon AI PRO R9700 (RDNA4, `gfx1201`) - OS/Image: `rocm/pytorch:rocm10.0_ubuntu26.04_py3.14_pytorch_release_2.13.0` (official AMD image) - torch: `2.13.0+rocm10.0.0` (+ `amd-torch-device-gfx1201==2.13.0+rocm10.0.0` installed alongside, required separately for any kernel to dispatch at all — see note below) - comfy-kitchen: `0.2.35` (also reproduced on `0.2.10`) - ComfyUI: current `master` as of 2026-09-20 - Custom nodes involved: `ComfyUI_MinimaxH3HybridLoader` / `ComfyUI-MiniMax-H3-Turbo` (MiniMax H3 video model, `int8_convrot`-quantized ~25GB text encoder + ~20GB diffusion transformer), but the same "Found quantization metadata version 1" / "Using MixedPrecisionOps for text encoder" hang has also been independently reported by other users loading unrelated models (e.g. Pixal3D/Trellis2) on the same `gfx1201` architecture — see "Related reports" below. So this does not appear to be model-specific. ## Steps to reproduce 1. On a `gfx1201` machine, install a clean `comfy-kitchen==0.2.35` + matching ROCm-10.0 torch stack (`torch`, `amd-torch-device-gfx1201`, `rocm-sdk-core`/`rocm-sdk-libraries`/`rocm-sdk-device-gfx1201`, all `==...+rocm10.0.0`/`10.0.0`). 2. Verify basic ops work (`torch.ones(4,4,device='cuda')+1`, fp16 matmul) — they do, confirming the base install is sound. 3. Load any model whose weights use `comfy.ops.MixedPrecisionOps` with `int8_convrot` (or plain `int8`) quantization and a non-trivial weight count (hundreds to low thousands of tensors). 4. Observe the log hang at: ``` Found quantization metadata version 1 Using MixedPrecisionOps for text encoder CLIP/text encoder model load device: cuda:0, offload device: cpu, current: cpu, dtype: torch.float16 Requested to load MiniMaxH3TEModel_ Model MiniMaxH3TEModel_ prepared for dynamic VRAM loading. 25882MB Staged. 0 patches attached. Force pre-loaded 310 weights: 1272 KB. ``` 5. `ps -o pid,etimes,time,pcpu` on the ComfyUI process shows a single thread pinned at ~90-100% CPU for the entire duration; `rocm-smi --showuse` shows ~0-3% GPU utilization the whole time. ## Root cause ```python >>> import comfy_kitchen >>> from comfy_kitchen.registry import registry >>> registry._priority ['hip', 'cuda', 'triton', 'eager'] >>> sorted(registry._capabilities['eager'] - registry._capabilities['hip']) ['dequantize_int8_convrot_weight', 'dequantize_int8_embedding', 'dequantize_int8_simple', 'dequantize_mxfp8', 'dequantize_nvfp4', 'prepare_int4_weight_for_int8_linear', 'quantize_mxfp8', 'quantize_nvfp4', 'rotate_int8_convrot_weight', 'scaled_mm_mxfp8', 'scaled_mm_nvfp4'] ``` `comfy_kitchen/backends/hip/__init__.py` defines `dequantize_int8_convrot_weight_dtype(...)` and `dequantize_int8_simple_dtype(...)`, but never registers the bare `dequantize_int8_convrot_weight` / `dequantize_int8_simple` names that `comfy/ops.py`'s model-loading path (`_load_quantized_module` → weight dequant/requant) actually calls. `comfy_kitchen`'s own `cuda` backend *does* implement both bare functions as one-line wrappers around the `_dtype` version with a `float32` default: ```python # backends/cuda/__init__.py def dequantize_int8_convrot_weight(q, scale, group_size) -> torch.Tensor: return dequantize_int8_convrot_weight_dtype(q, scale, group_size, DTYPE_TO_CODE[torch.float32]) ``` The `hip` backend has no equivalent — the bare name is simply absent from its `_build_constraints()` capability dict — so dispatch has no `hip` candidate for that function name and falls through the priority list to `eager`. ## Confirmed fix (partial) Adding the same one-line wrapper pattern to `comfy_kitchen/backends/hip/__init__.py` for both functions, and adding the corresponding entries to `_build_constraints()`, **does work** — verified directly: ```python >>> import comfy_kitchen >>> from comfy_kitchen.registry import registry >>> 'dequantize_int8_convrot_weight' in registry._capabilities['hip'] True >>> comfy_kitchen.backends.hip.dequantize_int8_convrot_weight(q, scale, 64) # runs on GPU, correct output ``` ```python # added to backends/hip/__init__.py, right after dequantize_int8_simple_dtype: def dequantize_int8_simple(q: torch.Tensor, scale: torch.Tensor) -> torch.Tensor: return dequantize_int8_simple_dtype(q, scale, DTYPE_TO_CODE[torch.float32]) # added right after dequantize_int8_convrot_weight_dtype: def dequantize_int8_convrot_weight(q: torch.Tensor, scale: torch.Tensor, group_size: int) -> torch.Tensor: return dequantize_int8_convrot_weight_dtype(q, scale, group_size, DTYPE_TO_CODE[torch.float32]) ``` (plus matching `"dequantize_int8_simple"` / `"dequantize_int8_convrot_weight"` entries in `_build_constraints()`, copied from the `_dtype` entries minus the `output_dtype_code` param.) **However, this only partially fixes the hang.** After applying it, the exact same model still hangs at the exact same log line for 30+ minutes. `dequantize_int8_embedding` has **no `hip` implementation at all** (not even a `_dtype` variant to wrap) — this is almost certainly the actual remaining bottleneck for text-encoder loading specifically, since text encoders have large embedding tables and this function has no accelerated path whatsoever on `hip`. That one needs an actual native implementation, not a wrapper — beyond what I can contribute myself. ## Note: a second, unrelated blocker on the way to reproducing this For anyone trying to reproduce: on a from-scratch ROCm 10.0 install, `torch.cuda.is_available()` returns `True` and the device name resolves correctly, but *every* kernel launch (including `torch.ones(4,4,device='cuda')+1`) fails with `hipErrorInvalidImage` unless `amd-torch-device-gfx1201==2.13.0+rocm10.0.0` is *also* installed alongside `torch`/`rocm-sdk-device-gfx1201` (it's a separate package carrying torch's external "kernel pack" for the exact arch — not obviously implied by the official install docs). Not a `comfy-kitchen` bug, just a prerequisite worth knowing before debugging anything else on this chip. ## Related reports (independent confirmation this is a `gfx1201`-wide pattern, not model-specific) - [Comfy-Org/ComfyUI#15001](https://github.com/Comfy-Org/ComfyUI/issues/15001) — same architecture (R9700/gfx1201), same "Found quantization metadata version 1" hang signature, different model (LTX 2.3). Root-caused there to mmap-backed `.to(device)` overhead (~0.5s/tensor fixed cost) rather than this dispatch gap — likely a **second, independent** slow-path on this architecture, not the same bug, since `--disable-mmap` did not resolve the hang reported here. - r/comfyui thread on running Pixal3D/Trellis2 via `comfy-kitchen`'s `hip` backend: works fine in 93.99s on RDNA3 (7900XT, `gfx1100`), but hangs for 3+ minutes on RDNA4 (RX 9070, `gfx1201`) in the same `_naf_hr` code path, with per-step timings showing the RDNA4-specific slowdown isolated to specific tensor-shape calls. Same generational pattern: RDNA3 fine, RDNA4 (`gfx1201`) broken. - [ROCm/TheRock#7992](https://github.com/ROCm/TheRock/issues/7992), [#7861](https://github.com/ROCm/TheRock/issues/7861), [#7839](https://github.com/ROCm/TheRock/issues/7839) — separate but related `gfx1201`/`gfx1151` kernel-dispatch regressions in the ROCm 10.0/torch 2.13 "kernel pack" architecture, `7.14.0` reported as the last known-good build across multiple of these. ## Ask - Add the missing bare `dequantize_int8_convrot_weight` / `dequantize_int8_simple` wrappers to the `hip` backend (patch above, happy to open a PR if useful). - A native (or wrapped) `hip` implementation of `dequantize_int8_embedding` would likely be needed to fully resolve model-loading hangs for models with quantized embedding tables (e.g. text encoders) on `gfx1201`. - More generally: is there a way to make dispatch gaps like this *visible* (e.g. a warning log the first time a function falls through to `eager` for a "should be accelerated" backend), rather than silent? This would have saved a lot of debugging time — the log gives no indication that a 25GB model is about to be processed through a generic Python fallback path. ## Note I see #183 (open, not yet merged) is already tuning the HIP INT8 ConvRot compute kernel specifically for gfx1200/gfx1201 with H3-shaped projections (`@qweqweewqe7-create`) — that's a different layer of the same code path (kernel geometry/perf, once dispatch actually reaches `hip`) rather than this dispatch-gap issue, but tagging for visibility since it's clearly the same problem area and hardware. Would be great to get these two efforts pointed at each other.
Author
Owner

@0xDELUXA commented on GitHub (Sep 22, 2026):

Thanks for the detailed write-up.

The capability gap does not appear to be the cause of the hang. On gfx1200 (RX 9060 XT) with comfy-kitchen 0.2.35, the eager/HIP difference reproduces exactly. However, none of those bare names are on the model-load path. comfy/ops.py's _load_quantized_module does not call into comfy-kitchen at all: it wraps the int8 weight and weight_scale in a QuantizedTensor and moves them to the device. With a hook on registry.get_capable_backend, loading 200 int8 ConvRot 3072x3072 layers through comfy.ops.mixed_precision_ops() took 434 ms and made zero comfy-kitchen dispatches.

At runtime, the int8 path already lands on HIP. The forward dispatches int8_linear -> hip, and TensorWiseINT8Layout.dequantize calls dequantize_int8_convrot_weight_dtype / dequantize_int8_simple_dtype, which also resolve to HIP. The bare dequantize_int8_simple / dequantize_int8_convrot_weight are only reached if called directly, so wrapping them on HIP is harmless parity but will not change load time.

The eager backend is also not a CPU fallback. It runs regular torch ops on the GPU tensors. A 5120x5120 ConvRot dequantize takes 3.8 ms on eager vs 0.96 ms on HIP, so even a model with around a thousand weights all going through eager would add seconds, not tens of minutes.

dequantize_int8_embedding has no CUDA implementation either; it is eager-only on every backend, and HIP mirrors CUDA's envelope here. It gathers only the requested rows on the GPU: 0.48 ms for 512 tokens from a 200k x 5120 int8 ConvRot table.

On making fallbacks visible: backend selection is already logged at debug level on the comfy_kitchen.dispatch logger, so logging.getLogger("comfy_kitchen.dispatch").setLevel(logging.DEBUG) shows every choice.

A single core pinned at 100% with the GPU idle right after "prepared for dynamic VRAM loading" points to host-side work rather than a kernel choice: safetensors mmap page-in, dynamic VRAM staging, or code-object loading from the ROCm 10 kernel pack (the TheRock issues you linked). The quickest way to pin it down is py-spy dump --pid <comfyui pid> a few times during the hang; the Python stack will show exactly where the thread is. Running once with --disable-dynamic-vram and once with --disable-mmap would also help separate those paths.

<!-- gh-comment-id:5781748685 --> @0xDELUXA commented on GitHub (Sep 22, 2026): Thanks for the detailed write-up. The capability gap does not appear to be the cause of the hang. On gfx1200 (RX 9060 XT) with `comfy-kitchen 0.2.35`, the eager/HIP difference reproduces exactly. However, none of those bare names are on the model-load path. `comfy/ops.py`'s `_load_quantized_module` does not call into `comfy-kitchen` at all: it wraps the int8 weight and `weight_scale` in a `QuantizedTensor` and moves them to the device. With a hook on `registry.get_capable_backend`, loading 200 int8 ConvRot 3072x3072 layers through `comfy.ops.mixed_precision_ops()` took 434 ms and made zero `comfy-kitchen` dispatches. At runtime, the int8 path already lands on HIP. The forward dispatches `int8_linear -> hip`, and `TensorWiseINT8Layout.dequantize` calls `dequantize_int8_convrot_weight_dtype` / `dequantize_int8_simple_dtype`, which also resolve to HIP. The bare `dequantize_int8_simple` / `dequantize_int8_convrot_weight` are only reached if called directly, so wrapping them on HIP is harmless parity but will not change load time. The eager backend is also not a CPU fallback. It runs regular torch ops on the GPU tensors. A 5120x5120 ConvRot dequantize takes 3.8 ms on eager vs 0.96 ms on HIP, so even a model with around a thousand weights all going through eager would add seconds, not tens of minutes. `dequantize_int8_embedding` has no CUDA implementation either; it is eager-only on every backend, and HIP mirrors CUDA's envelope here. It gathers only the requested rows on the GPU: 0.48 ms for 512 tokens from a 200k x 5120 int8 ConvRot table. On making fallbacks visible: backend selection is already logged at debug level on the `comfy_kitchen.dispatch` logger, so `logging.getLogger("comfy_kitchen.dispatch").setLevel(logging.DEBUG)` shows every choice. A single core pinned at 100% with the GPU idle right after "prepared for dynamic VRAM loading" points to host-side work rather than a kernel choice: safetensors mmap page-in, dynamic VRAM staging, or code-object loading from the ROCm 10 kernel pack (the TheRock issues you linked). The quickest way to pin it down is `py-spy dump --pid <comfyui pid>` a few times during the hang; the Python stack will show exactly where the thread is. Running once with `--disable-dynamic-vram` and once with `--disable-mmap` would also help separate those paths.
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Comfy-Org/comfy-kitchen#56
No description provided.