Skip to content

Feature/qwen35 tp fused prefill ggml backend - #103

Merged
zhongkaifu merged 3 commits into
mainfrom
feature/qwen35_tp_fused_prefill_ggml_backend
Jul 29, 2026
Merged

Feature/qwen35 tp fused prefill ggml backend#103
zhongkaifu merged 3 commits into
mainfrom
feature/qwen35_tp_fused_prefill_ggml_backend

Conversation

@zhongkaifu

Copy link
Copy Markdown
Owner

No description provided.

zhongkaifu and others added 3 commits July 29, 2026 01:31
…ckend

TP >= 2 prefill ran a per-layer loop that round-tripped the [N, hidden]
activation through host memory several times per layer, measuring 2.4x
SLOWER than TP=1 (9B Q8: 612 vs 1459 tok/s at N=512). This adds the
whole-model fused TP prefill: tp_mode in the Qwen3.5 verify kernel builds
one N-token graph per rank over that rank's shards (packed GDN in-proj,
in-graph KV append, MRoPE, expert-parallel MoE, column-parallel LM head)
and returns a segmented plan the existing tp_execute_plans driver runs
with ~2 AllReduce cut points per layer — the same architecture as the
fused TP decode and Gemma4's TP verify.

MoE routing under expert parallelism runs a per-rank top-k over
mask-zeroed router probabilities instead of remapping the global top-k
through an id LUT: ggml's batched mul_mat_id paths (CUDA mm_ids_helper
and the generic fallback) require the ids within one token to be
DISTINCT, and mapping every foreign route to a filler id silently
scrambled expert outputs at N > 1 (fluent but wrong text on the 35B).
The rank-local top-k always yields distinct owned experts; gating
weights keep exactly the global top-k members (prob >= the k-th largest
global prob, read through a DESC argsort since CUDA's TOP_K output is
unsorted), so the ranks' partials sum to the single-GPU MoE.

The per-layer GDN states ride back to the host through the plan's new
extra_out downloads; the driver then invalidates the host-keyed device
copies so the fused decode re-uploads the post-prefill state.

Measured on 2x RTX 2000 Ada, ggml_cuda, prefill/decode tok/s:
  Qwen3.5-9B Q8      TP=1: 1469/22.7   TP=2: 612/41 -> 2051/41 (N=512)
                                       TP=2: 2365/40 (N=2048)
  Qwen3.5-35B-A3B IQ4 TP=2: ~100/42 -> 1702/42 (N=512), 2122/42 (N=2048)

Output verified against TP=1 / pre-change baselines: short prompts,
2K-token document recall, multi-turn KV reuse, and image+MRoPE
multimodal prefill all match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Nemotron's TP MoE fed the flattened [seqLen, numExperts] router logits
to SelectNemotronTopKExperts as if they were ONE token's row, indexing
the router bias out of bounds on any multi-token prefill (TP >= 2
crashed on the first prompt) and applying a single expert set to the
whole chunk. Route each token independently and bucket the assignments
by expert so each expert runs one batched matmul over its tokens — the
same dispatch shape as the gpt-oss TP MoE. The router now also computes
once on rank 0 instead of per rank.

Qwen3.5 TP prefill: bias the mask-zeroed router probabilities so an
owned expert always outranks a foreign one when softmax underflows to
exactly zero — a zero-for-zero tie in top_k could select a foreign
index and reintroduce the duplicate-ids hazard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ggml-vulkan's GET_ROWS asserts zero buffer-offset remainders, so an
element-offset view of the argsort output cannot be its id input (the
k-th-column gather aborted ggml_vk_build_graph). Gather all sorted
probabilities through the whole (aligned) argsort tensor once and take
w_min / the top-k normalizer as views of the RESULT — elementwise ops
and cont carry offsets fine on every backend. Verified on ggml_vulkan
--tp 2 (35B fused prefill active, output matches CUDA) and re-verified
on ggml_cuda.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@zhongkaifu
zhongkaifu merged commit 65e1f1e into main Jul 29, 2026
1 check failed
@zhongkaifu
zhongkaifu deleted the feature/qwen35_tp_fused_prefill_ggml_backend branch July 29, 2026 13:53
@github-actions

Copy link
Copy Markdown

Engine comparison — TensorSharp vs llama.cpp (PR smoke)

No report artifact was produced — the benchmark failed before generating results (see the workflow logs).

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.

1 participant