perf(qwen35): use in-place GDN state write in pure AR

Draft
DU
3mo ago
+168-33

Summary

Dense (27B) qwen35 AR-decode performance stack: in-place GDN state write, 256-token FA-bucket CUDA-graph reuse (with a props-check skip), FWHT K-rotation gate for q4_0 KV, swiglu fuse, and concat fast-paths. Rebased clean onto current main.

Supersedes #476, whose micro-opts are a subset of this stack.

Dependency (resolved)

The in-place GDN op itself (ggml_gated_delta_net_inplace, originally Lucebox ggml #24/#27) is now vendored into main via #499 (5e302cbb, "port(ggml): in-place GDN state write + qwen dense decode recovery"). main ships the op but still calls the non-inplace variant at the qwen call site (qwen35_target_graph.cpp:941); this PR wires the qwen35 dense pure-AR consumer to use it (ce2325aa). After the ggml-only vendorize (#486), there is no external submodule bump left — the earlier "draft until the ggml submodule commit is available" blocker no longer applies. This branch was rebased onto current main, dropping the obsolete submodule-gitlink bumps.

Commits

  • ce2325aa use in-place GDN state write in pure AR
  • e1ce9c73 fuse silu+mul → swiglu_split in DeltaNet gated output
  • 96fd3ea9 gate FWHT rotation for q4_0 KV + skip repeat_back in pure AR
  • 5fd7c3fb cont concat inputs for fast concat_cont path
  • a5915e29 graph reuse in AR decode — skip rebuild within 256-token FA bucket
  • a8253730 reduce concat cont overhead — materialize qkv_T once
  • 9d450624 recover dense AR replay fast path

Correctness (verified)

  • Adversarial static review (Codex): clean — no silent-corruption, race, or aliasing across all 7 commits. Checked with file:line: 256-bucket boundary condition, in-place GDN aliasing, FWHT K/Q rotation symmetry, swiglu numerical equivalence.
  • AR-replay bit-identity: DFLASH_AR_NO_REUSE ON vs OFF → byte-identical greedy output at 4K and 16K (16K crosses two bucket boundaries). The 256-bucket rebuild guard re-captures exactly when the FA window advances; the props-check skip is a launch-overhead optimization only, not a numerics change.
  • In-place GDN bit-identity: revert-ce2325aa A/B with WHT held constant → byte-identical. Output-inert when live.
  • FWHT-skip fidelity: teacher-forced over 32K of real corpus, bucketed vs an f16 reference — top-1 agreement gap ≤ 0.6pp (flat with depth) and PPL flips direction bucket-to-bucket → within noise. Skipping the K-rotation on q4_0 does not measurably degrade quality on real text.

Performance

27B dense, q4_0 KV, pure AR, temp 0, HTTP serving path, median of N post-warmup, same binary both arms (accept_rate=0, prefix_len=0 verified — no caching asymmetry). AR graph-reuse, replay ON vs OFF:

contextreplay ON (tok/s)replay OFF (tok/s)Δ decode
3.1K41.2540.70+1.35%
14.1K38.4537.75+1.85%
30.6K34.9034.35+1.60%
71K29.1528.70+1.57%
114K24.5524.10+1.87%

The graph-reuse decode gap is a flat ~1.6% band across a 37× context range (3K→114K) and never clears each arm's own run-to-run spread (e.g. at 71K, ON's own min–max is 2.7% vs a 1.57% gap). It does not grow with depth, contradicting the "launch overhead scales with KV length → win grows with context" rationale. prefill throughput and wall are statistically identical between arms (the reuse only touches per-token decode launch).

The optimization's originally reported ~+6% (106→113 tok/s) was measured on Q3_K_XL at 16K; it does not reproduce on Q4_K_M at any depth tested here (3K/16K/32K/71K/114K). Read this as: correctness-neutral and safe on Q4_K_M, but no HTTP-measurable decode win on this quant — the speedup appears quant-specific.

Notes

  • Rebase resolved the stale llama.cpp submodule-gitlink vs the ggml vendorize on main (dropped the obsolete gitlink bumps; rerere combined main's src[7] persist-wiring with the pure_ar in-place op call — verified bit-identical).
  • Kept as draft pending review.