Skip to content

TEMP: refresh ROCm qwen35moe integration to upstream #131 bb432e8 - #40

Merged
nekomario28 merged 2 commits into
work/rocm-qwen35moe-refresh-bb432e8-20260826from
donor/upstream-131-bb432e8
Aug 26, 2026
Merged

TEMP: refresh ROCm qwen35moe integration to upstream #131 bb432e8#40
nekomario28 merged 2 commits into
work/rocm-qwen35moe-refresh-bb432e8-20260826from
donor/upstream-131-bb432e8

Conversation

@nekomario28

Copy link
Copy Markdown
Owner

Temporary internal history-preserving refresh carrier. Base is exact combined ROCm×qwen35moe head 2195d02. Head is exact upstream FlashML-org#131 donor bb432e8 imported into the fork. Purpose: detect/resolve only real overlap with the already validated ROCm/DSV4 integration before spending another native GGUF compile. Close after the refreshed exact head is validated and promoted.

Three defects with one root: "gguf" is a container tag, not a weight layout.
The checkpoint picks a ggml type per tensor and the concrete CPU format has to
be recovered from the bank types, but two call sites tested the tag directly.

_cpu_moe_executor_viable compared expert_quant against _WFMT_IDS, which answers
False for EVERY GGUF checkpoint. That silently disabled the automatic residency
split on hosts where CUDA pinning is quota-capped -- WSL caps it near 40% of RAM
(measured: 81.78 GiB of 204). The symptom was not a clear refusal but
cudaHostRegister failing partway through the banks, which reads as a memory
shortage rather than a dispatch gap.

gemm1_dot handled bf16 and q4_0 and then FELL THROUGH to the NVFP4 path, which
dereferences scale/global pointers that are null for GGUF banks. An unhandled
format therefore segfaulted inside a worker thread with no Python traceback.
It now raises through TORCH_CHECK naming the format, same reasoning as the
kernel default: guards in FlashML-org#138: an unhandled case that reads null or
uninitialised memory is far worse than one that errors.

GGUFEmbedding dequantized unconditionally, but the unquantized types are raw
value bytes with no dequant kernel at all (ggml_dequantize rejects type 1
outright). DeepSeek-V4 ships token_embd as F16 and died on the first lookup.
The gathered rows are now reinterpreted for those types, matching the fix
already applied to fused_mul_mat_gguf.
Five fixes that stood between a reconciled adapter and a generated token, found
by loading the real 164GB antirez/deepseek-v4-gguf Q4KExperts checkpoint.

convert_deepseek4_to_gguf was never called from the model's __init__, so the
fp8 Linears were never swapped and load_state_dict demanded a wq_a.scale no
GGUF tensor can fill. The reconciliation test passed because it called the
converter explicitly; the engine does not. Third time this hook has been missed
in this package (gemma4 and qwen35moe before it).

GGUFLinearNN passed its input straight to fused_mul_mat_gguf, which takes
[tokens, in_features] and treats dim 0 as the batch. F.linear -- which it
replaces -- accepts any number of leading dims, and deepseek_v4 relies on that:
its attention passes 3-D tensors. Collapsing one silently reshaped q so the
sparse-attention kernel's `b, m, h, d = q.shape` unpack failed. Leading dims are
now folded and restored.

GGUFEmbeddingNN dequantized unconditionally; token_embd is F16 here, which has
no dequant kernel. Reinterpreted instead.

The DSV4 sparse-attention kernel exceeded Turing's 64KB shared-memory block
limit. BLOCK_T was tuned for sm_120's ~99KB budget, but the dominant cost is not
the KV tile: q and acc are [BLOCK_H, D] in fp32, which at BLOCK_H=16 and
head_dim 512 is 65536 B on its own -- an entire Turing block before a single KV
byte, which is why shrinking BLOCK_T alone left the requirement stuck at 66624.
BLOCK_H, BLOCK_T and num_stages are now chosen from the device's opt-in shared
memory; sm_80 and above are unchanged.

_TOKENIZER_ARCH mapped deepseek4 to "llama". The file says
tokenizer.ggml.model = gpt2: the llama converter is sentencepiece-shaped and
encodes a space as U+2581, so against a GPT2-BPE vocab every space was silently
DROPPED on detokenization ("ThecapitalcityofFranceisParis"). The model was
correct throughout; only the detokenizer was wrong, which presents as model
damage and is not. Mapped to the qwen2 (GPT2-BPE) entry.

Verified on a Quadro RTX 6000 (Turing, sm_75, 24GB) with 204 GiB of WSL RAM:
145 GiB of expert banks split 24 layers GPU-pinned / 19 OS-locked for CPU
decode, CPU executor on the Q4_K kernels, ~18GB VRAM. At temperature 0:
  "The capital city of France is" -> " Paris. The capital city of England is
   London..."
  "The largest planet in our solar system is" -> " Jupiter. It is so big that
   more than 1,300 Earths"

Known limitation: CUDA graph capture crashes the worker on this configuration,
so this runs with --cuda-graph-max-bs 0. Decode is slower than it should be as
a result. Not yet diagnosed.
@nekomario28
nekomario28 merged commit 93de0aa into work/rocm-qwen35moe-refresh-bb432e8-20260826 Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants