Repository navigation
graph: six fusions for the hybrid-attention decode graph (-25% ops/token; Metal 2B +7.7%, 27B +2.6% decode; CUDA 9B +2.5%) - #165
Conversation
Every folded weight's activation went through three encoded ops: an elementwise MUL by the +-1 sign vector, a RESHAPE and the FWHT-hint MUL_MAT. The sign factors are exact, so the transform now applies them while loading its row and the MUL is elided, matching what the CUDA backend already does. The encode list drops empty ops, so the MUL / RESHAPE / MUL_MAT pattern is matched on the graph's own indices and checked with ggml_can_fuse_subgraph_ext; the fused kernel writes the matmul's output, so its ranges are checked for concurrency explicitly. Measured on an M5 Pro, largest band PQ2_0 folded (block 1024), r=3 x2 interleaved: encoded nodes per decoded token 2359 -> 2103, tg128 27.05 -> 27.45 (+1.5%), pp512 390.5 -> 397.2 (+1.7%). Greedy output byte-identical over 3 prompts; test-backend-ops MUL_MAT_HADAMARD 24/24.
The hybrid-attention layers spent four elementwise launches per layer per token on the gate scalars: sigmoid(beta), and softplus(alpha + dt_bias) * A for the decay. ggml_gated_delta_net_set_raw_gates() marks a gated_delta_net op as taking beta and alpha pre-activation, with dt_bias and A as two extra sources, and the kernel applies the same formulas as the unary kernels while reading the scalars it already read. The qwen35 builder hands the fused op the raw projections (GGML_GDN_RAW_GATES_DISABLE=1 restores the separate ops); the non-fused chunked path keeps the activated tensors. CPU reference and Metal kernel implemented here; CUDA follows in the next commit. test-backend-ops -o GATED_DELTA_NET on MTL0: 39/39 incl. 4 raw-gate cases (decode, 2 seqs, prefill 64, rows mode). Encoded nodes per decoded token on the largest band folded PQ2_0: 2103 -> 1910. Greedy output byte-identical to base. M5 Pro, r=3 x2 interleaved, both fusions vs base: 2B tg128 206.4 -> 214.8 (+4.1%), pp512 +1.0%; largest band tg128 27.08 -> 27.55 (+1.7%), pp512 +1.3%.
Same folding as the Metal and CPU implementations: with the op's raw-gate flag set, beta goes through sigmoid and the decay is raw_a[h] * softplus(g + raw_dt_bias[h]) inside the kernel, using the formulas of the unary kernels. The template gains a RAW parameter (scalar gate only; KDA asserts it off) and the two tables are passed as extra pointers. Compile-checked only by inspection here; needs a CUDA build plus test-backend-ops -o GATED_DELTA_NET before this branch is pushed.
…he builder on device backend SYCL, OpenCL, WebGPU and Hexagon compute gated_delta_net but do not implement the raw-gate flag, so their supports_op now declines such ops (the CPU fallback is correct). The qwen35 builder only emits raw gates when every device is CPU, Metal, CUDA, ROCm or MUSA, so those backends keep their native op instead of a CPU fallback for the whole recurrence.
silu(z) * rms_norm(o) was a unary and a mul; ggml_swiglu_split computes the same expression in one kernel. Encoded nodes per decoded token on the largest folded band PQ2_0: 1910 -> 1862. Greedy output byte-identical. M5 Pro decode with the three graph changes so far vs base: 2B 205.3 -> 219.9 tok/s (+7.1%), largest band 27.12 -> 27.72 (+2.2%).
q and k occupy adjacent, equally shaped head groups in the conv output, so a single l2_norm over the joint view replaces two launches per recurrent layer; q and k become views into its result. Encoded nodes per decoded token on the largest band: 1862 -> 1814. Greedy output byte-identical.
When the conv output is consumed only by a silu, the conv kernels apply it on store (function constant) and write the silu's tensor directly, matching what the CUDA backend already does. Encoded nodes per decoded token on the largest band: 1814 -> 1766. test-backend-ops -o SSM_CONV 45/45; greedy output byte-identical. Cumulative decode vs base on an M5 Pro: 2B 207.0 -> 223.0 tok/s (+7.7%), largest band 27.15 -> 27.85 (+2.6%).
The fused-GDN state-rows check and the raw-gates check each looped over the model's devices on every recurrent layer, asking the backend for the device type each time. Both flags are now computed once in the graph constructor.
They had been appended to the perf-mode list, so test mode never ran them.
|
Tested on both backends, PR head 6e92bc0 vs its merge-base 9294043 (prism with #160), separate worktrees, Correctness
CUDA L40S, tg128, mean of two interleaved passes
Prefill flat within noise. Same PR binary with Metal M5 Max, tg128, first pass (runs spaced 100 s to avoid throttling; second pass and KLD still running, will append if anything moves)
The 4B (block 512) gains most on Metal, ahead of the 2B figure in the description; Metal error bars here are 0.1 to 2 t/s. Review notes: op_params[1] on GATED_DELTA_NET was unused before, the fused sigmoid / softplus use the unary kernels' exact formulas, the Metal sign-flip fusion checks the MUL, RESHAPE and hinted MUL_MAT form a closed subgraph and re-runs the concurrency check on the real output, and the non-implementing backends decline the raw-gate op in |
Resolves the conflicts with PrismML-Eng#160 (PTQ1_0 mmvq multi-column path uses the compile-time gate flag) and PrismML-Eng#165 (gated_delta_net kernel carries both the raw-gates flag and the GB10 exp(g) precompute flag; the precompute only engages when raw gates are off).
Pass raw beta and alpha projections to GDN on supported devices. Normalize the adjacent Q and K head groups together and express gated RMS norm as one SwiGLU operation. Keep activated gates for the chunked path. Based on bri-prism's model-graph fusions in PrismML-Eng#165. Source: PrismML-Eng@00e8187 Assisted-by: GitHub Copilot Assisted-by: Codex (porting and rebasing) Ported and rebased by Codex.
Apply sigmoid and softplus inside the recurrent shader. Read the selected cache row directly while retaining snapshot output and rollback layout. Validate row indices, strides, types and buffer bounds before dispatch. Implement the Vulkan path for Pasha Khosravi's indexed-state API and bri-prism's raw-gate fusion, using the existing Vulkan GDN kernel. Inspired-by: PrismML-Eng#165 Inspired-by: PrismML-Eng@bed6e9a Assisted-by: Codex
Fuse residual normalization, activation quantization and shared-Q8 projections. Add transpose concat, recurrent gate activation and scale combinations, with the original GB10 dispatch controls. Preserve portable device queries and PTX guards, and adapt fused RMS launches to the current channel and sample dimensions. Source: PrismML-Eng#135 Source: PrismML-Eng#163 Source: PrismML-Eng#165 Source: PrismML-Eng#168 Source commits (PrismML-Eng/llama.cpp): 3dd7675 00e8187 d8d96cf d8f26ee Co-authored-by: bri-prism <288398250+bri-prism@users.noreply.github.com> Co-authored-by: Pasha Khosravi <khosravipasha@users.noreply.github.com> Co-authored-by: Pasha Khosravi <pasha@prismml.com> Assisted-by: GitHub Copilot Assisted-by: OpenAI Codex Ported and rebased by Codex.
Enable indexed state on Metal and raw gates on Metal/CUDA/ROCm/MUSA. Use Brian (bri-prism)'s separate capability checks and preserve the CPU and Vulkan paths. The Vulkan extensions were authored by Codex. Source: PrismML-Eng#165 Original-commit: 00e8187 Source: c1f65ed Source: 631528b Assisted-by: GitHub Copilot Assisted-by: OpenAI Codex Ported and rebased by Codex.
Pass raw beta and alpha projections to GDN on supported devices. Normalize the adjacent Q and K head groups together and express gated RMS norm as one SwiGLU operation. Keep activated gates for the chunked path. Based on bri-prism's model-graph fusions in PrismML-Eng#165. Source: PrismML-Eng@00e8187 Assisted-by: GitHub Copilot Assisted-by: Codex (porting and rebasing) Ported and rebased by Codex.
Apply sigmoid and softplus inside the recurrent shader. Read the selected cache row directly while retaining snapshot output and rollback layout. Validate row indices, strides, types and buffer bounds before dispatch. Implement the Vulkan path for Pasha Khosravi's indexed-state API and bri-prism's raw-gate fusion, using the existing Vulkan GDN kernel. Inspired-by: PrismML-Eng#165 Inspired-by: PrismML-Eng@bed6e9a Assisted-by: Codex
Fuse residual normalization, activation quantization and shared-Q8 projections. Add transpose concat, recurrent gate activation and scale combinations, with the original GB10 dispatch controls. Preserve portable device queries and PTX guards, and adapt fused RMS launches to the current channel and sample dimensions. Source: PrismML-Eng#135 Source: PrismML-Eng#163 Source: PrismML-Eng#165 Source: PrismML-Eng#168 Source commits (PrismML-Eng/llama.cpp): 3dd7675 00e8187 d8d96cf d8f26ee Co-authored-by: bri-prism <288398250+bri-prism@users.noreply.github.com> Co-authored-by: Pasha Khosravi <khosravipasha@users.noreply.github.com> Co-authored-by: Pasha Khosravi <pasha@prismml.com> Assisted-by: GitHub Copilot Assisted-by: OpenAI Codex Ported and rebased by Codex.
Enable indexed state on Metal and raw gates on Metal/CUDA/ROCm/MUSA. Use Brian (bri-prism)'s separate capability checks and preserve the CPU and Vulkan paths. The Vulkan extensions were authored by Codex. Source: PrismML-Eng#165 Original-commit: 00e8187 Source: c1f65ed Source: 631528b Assisted-by: GitHub Copilot Assisted-by: OpenAI Codex Ported and rebased by Codex.
Pass raw beta and alpha projections to GDN on supported devices. Normalize the adjacent Q and K head groups together and express gated RMS norm as one SwiGLU operation. Keep activated gates for the chunked path. Based on bri-prism's model-graph fusions in PrismML-Eng#165. Source: PrismML-Eng@00e8187 Assisted-by: GitHub Copilot Assisted-by: Codex (porting and rebasing) Ported and rebased by Codex.
Apply sigmoid and softplus inside the recurrent shader. Read the selected cache row directly while retaining snapshot output and rollback layout. Validate row indices, strides, types and buffer bounds before dispatch. Implement the Vulkan path for Pasha Khosravi's indexed-state API and bri-prism's raw-gate fusion, using the existing Vulkan GDN kernel. Inspired-by: PrismML-Eng#165 Inspired-by: PrismML-Eng@bed6e9a Assisted-by: Codex
Fuse residual normalization, activation quantization and shared-Q8 projections. Add transpose concat, recurrent gate activation and scale combinations, with the original GB10 dispatch controls. Preserve portable device queries and PTX guards, and adapt fused RMS launches to the current channel and sample dimensions. Source: PrismML-Eng#135 Source: PrismML-Eng#163 Source: PrismML-Eng#165 Source: PrismML-Eng#168 Source commits (PrismML-Eng/llama.cpp): 3dd7675 00e8187 d8d96cf d8f26ee Co-authored-by: bri-prism <288398250+bri-prism@users.noreply.github.com> Co-authored-by: Pasha Khosravi <khosravipasha@users.noreply.github.com> Co-authored-by: Pasha Khosravi <pasha@prismml.com> Assisted-by: GitHub Copilot Assisted-by: OpenAI Codex Assisted-by: Claude (Opus 5.5), rebase onto ggml-org master with conflict resolution Ported and rebased by Codex.
Enable indexed state on Metal and raw gates on Metal/CUDA/ROCm/MUSA. Use Brian (bri-prism)'s separate capability checks and preserve the CPU and Vulkan paths. The Vulkan extensions were authored by Codex. Source: PrismML-Eng#165 Original-commit: 00e8187 Source: c1f65ed Source: 631528b Assisted-by: GitHub Copilot Assisted-by: OpenAI Codex Ported and rebased by Codex.
What
Six graph-level changes that cut the encoded ops per decoded token on the hybrid-attention models by 25% (2,359 to 1,766 on the largest folded band) without changing any result bit:
ggml_gated_delta_net_set_raw_gates()lets the fused op take beta and alpha pre-activation with dt_bias and A as extra sources, applying sigmoid andA * softplus(alpha + dt_bias)in its prologue with the unary kernels' formulas. The qwen35 builder uses it on the fused-op paths only; the chunked path keeps the activated tensors. Backends that compute GDN but do not implement the flag (SYCL, OpenCL, WebGPU, Hexagon) decline it insupports_op, and the builder only emits it when every device is CPU/Metal/CUDA/ROCm/MUSA.GGML_GDN_RAW_GATES_DISABLE=1restores the separate ops.ggml_swiglu_splitinstead of silu + mul.l2_normover the adjacent q and k head groups, q and k as views of the result.Verification (M5 Pro, prism 8c0170d base, separate worktrees, r=3 x2 interleaved)
Correctness:
test-backend-ops -b MTL0-o GATED_DELTA_NET39/39 (4 new raw-gate cases against the CPU reference),-o MUL_MAT_HADAMARD24/24,-o SSM_CONV45/45. Greedy output byte-identical to base on the largest folded band for 3 prompts x 64 tokens and a 230-token prompt on both the smallest and largest bands (exercises the batched conv, chunked GDN and prefill FWHT paths).Per change on the 2B decode: sign+FWHT +2.8%, raw gates +1.3%, swiglu gating +3%, l2 pair ~0, conv+silu +1.6%.
CUDA (H100 PCIe, CUDA 12.x, base 8c0170d vs branch, separate trees, sheet flags, 2 passes)
test-backend-ops -b CUDA0:-o GATED_DELTA_NET39/39 with the 3 applicable raw-gate cases OK (the rows-mode one is not supported on CUDA, as before),-o MUL_MAT_HADAMARD27/27,-o SSM_CONV45/45,-o L2_NORM20/20. Greedy output identical to base on the 9B and 2B (48 tokens).GGML_GDN_RAW_GATES_DISABLE=1CUDA already fused the sign flip and the conv silu, so the CUDA gain is the raw gates plus the two graph-level ops; the 2B numbers sit inside that host's run-to-run spread.
The two device-scan loops in the qwen35 builder are also hoisted to the graph constructor (one scan per graph build), as suggested in the #163 review.