@@ -5,12 +5,16 @@ Vulkan backend across the model set** tested on AMD Strix Halo (Radeon 8060S /
55RADV GFX1151, Mesa 25.0.7, Vulkan 1.4.309, 96 GB UMA). It consolidates the
66original Qwen3-Coder-Next enablement work with the later multi-model fixes
77(Qwen3.5-122B-A10B GPU cross-ring, Qwen3.6-35B-A3B stochastic-sampling
8- corruption) and the per-model status/performance for Qwen3.6-27B and Gemma 4.
8+ corruption) and the per-model status/performance for Qwen3.6-27B, Gemma 4, and
9+ the 256K stress test results.
910
1011It replaces the scattered investigation notes that were created while debugging
1112the original 0% draft-token acceptance problem and the later qwen35moe
1213recurrent-state regressions.
1314
15+ ** Current PR head** : ` d6531e021 ` (` enable-dflash-qwen3-coder-next-on-vulkan ` )
16+ ** PR** : [ Anbeeld/beellama.cpp #79 ] ( https://github.com/Anbeeld/beellama.cpp/pull/79 )
17+
1418## Summary
1519
1620DFlash on Vulkan was enabled and stabilized across a range of target/drafter
@@ -23,7 +27,7 @@ conflict.
2327| Qwen3.6-27B | ` qwen35 ` (dense) | Qwen3.6-27B-DFlash | worked | (no fix needed; performance characterized) |
2428| Qwen3.5-122B-A10B | ` qwen35moe ` | qwen35-122b-a10b-dflash | GPU cross-ring recurrent-state desync under ` n_rs_seq=0 ` | Keep ` n_rs_seq ` for QWEN35MOE (RS partial rollback) instead of clamping to 0 |
2529| Qwen3.6-35B-A3B | ` qwen35moe ` | qwen36-35b-a3b-dflash | gibberish under stochastic sampling (0% acceptance) | Rotate DeltaNet RS snapshots for ` n_seq_tokens < K ` |
26- | Gemma 4 31B | ` gemma4 ` | gemma4-31b-it-dflash | worked; "own own own" under greedy, coherent under ` --reasoning on ` /temp=1.0 | (no runtime fix needed; sampling guidance) |
30+ | Gemma 4 31B | ` gemma4 ` | gemma4-31b-it-dflash | works (FA off); FA on → " own" loop (Vulkan FA kernel ISWA bug) | ` --flash-attn off ` workaround; stochastic less sensitive |
2731
2832The two decisive runtime fixes layered on top of the coder-next enablement are:
2933
@@ -66,6 +70,7 @@ The two decisive runtime fixes layered on top of the coder-next enablement are:
6670| Qwen3.6-27B (dense) | 12.47 | 29.80 (0.835) | 29.94 (0.816) |
6771| Qwen3-Coder-Next (MoE) | 53.26 | 34.07 (0.754) | 50.09 (0.621) |
6872| Qwen3.5-122B-A10B (MoE) | 13.92 | 8.20 (0.189) | 14.92 (0.256) |
73+ | Gemma 4 31B | 11.34 | 17.57 (0.231) | 16.93 (0.185) |
6974
7075Interpretation:
7176
@@ -79,10 +84,14 @@ Interpretation:
7984 dominates. ring=1 is at or slightly above baseline; ring=0 is slower (CPU
8085 capture overhead). These were already coherent; the fixes preserve that and
8186 make their stochastic-sampling paths coherent too.
87+ - ** Gemma 4 31B** (` gemma4 ` , no DeltaNet recurrent state): DFlash is coherent
88+ at ~ 1.5× speedup (11.34 → 17.57 tok/s) with ` --flash-attn off ` . ring=0 (CPU
89+ capture) beats ring=1 for Gemma 4's large ` n_embd ` (5376). See the Gemma 4
90+ section for the Vulkan DFlash + FA corruption issue.
8291- ** Gemma 4 31B** (` gemma4 ` , no DeltaNet recurrent state) is unaffected by the
83- recurrent-state fixes: ring=0 ~ 26.5 tok/s, ring=1 ~ 21.4, both coherent under
84- ` --reasoning on ` /temp=1.0. On Vulkan, ring=0 (CPU capture) beats ring=1 for
85- Gemma 4's large ` n_embd ` (5376), consistent with the quickstart guidance .
92+ recurrent-state fixes. On Vulkan, ring=0 (CPU capture) beats ring=1 for Gemma 4's
93+ large ` n_embd ` (5376). See the Gemma 4 section below for the Vulkan DFlash + FA
94+ corruption issue and its workaround ( ` --flash-attn off ` ) .
8695
8796## Qwen3-Coder-Next enablement (` qwen3next ` MoE)
8897
@@ -417,40 +426,68 @@ Vulkan produces "own own own" repetition with extremely low vocabulary diversity
417426(3% unique words over 512 tokens). The same prompt with ` --flash-attn off ` is
418427fully coherent (64% unique words, correct C++ code).
419428
429+ ** Evidence table** (hard prompt, ~ 665 tokens, 512 generated, greedy ` --temp 0 ` ):
430+
431+ | Configuration | Result | Vocabulary diversity |
432+ | ---| ---| ---|
433+ | G4 baseline + FA on | ✅ Coherent C++ code | 66% unique words |
434+ | G4 + DFlash + FA ** off** | ✅ Coherent C++ code | 64% unique words |
435+ | G4 + DFlash + FA ** on** | ❌ "own" repetition | 3% unique words |
436+
420437** Root cause** : The Vulkan flash attention kernel produces incorrect attention
421- weights during the DFlash draft-verification batch for Gemma 4's ISWA (Interleaved
422- Sliding Window Attention) layers. Gemma 4 has a mix of SWA and FULL attention
423- layers (` swa=4 full=2 ` ), and the Vulkan FA kernel does not handle the
424- attention mask correctly for the mixed SWA/FULL pattern under speculative
425- decoding's draft-verification batch structure. This is consistent with:
426-
427- - Known Vulkan FA kernel bugs: upstream PRs #12720 (unclamped mask loads),
428- #12853 (aligned mask loads) fixed mask stride issues, but the underlying
429- shader may still miscompute for the ISWA + draft-verification pattern.
430- - Known Vulkan + Gemma 4 corruption: ollama #15261 documents garbled output on
431- Vulkan + Gemma 4 (fp16 MMQ precision bug, fixed in ollama v0.30.0-rc31).
432- The issue was an fp16 precision bug in the Vulkan quantized-matmul (MMQ)
433- kernel — Vulkan is explicitly noted as producing wrong answers for Gemma 4
434- under certain conditions.
435- - The bug is specific to the FA kernel path during DFlash verification:
436- - Baseline (no DFlash) + FA: ** coherent** (correct C++ code, 66% unique words)
437- - DFlash + FA on: ** "own" repetition** (3% unique words, 499 tokens)
438- - DFlash + FA off: ** coherent** (correct C++ code, 64% unique words)
439-
440- The FA kernel works fine for normal decoding (baseline is correct), but during
441- DFlash draft verification the kernel computes wrong attention weights for the
442- ISWA layers, causing the target model to accept garbage draft tokens that
443- converge to "own" repetition.
444-
445- ** Workaround** : Use ` --flash-attn off ` for Gemma 4 DFlash on Vulkan, or use
446- stochastic sampling (` --temp 1.0 --top-k 64 --top-p 0.95 ` ) which is less
447- sensitive to the numerical precision issue. The corruption only appears under
448- greedy sampling (` --temp 0 ` ) because the model's top token is the corrupted
449- "own" token.
450-
451- ** Note** : This is a Vulkan backend limitation, not a DFlash logic bug. On CUDA
452- the issue does not reproduce because the CUDA FA kernel handles ISWA layers
453- correctly.
438+ output during DFlash draft-verification for Gemma 4's ISWA (Interleaved Sliding
439+ Window Attention) layers. The precise mechanism is likely a combination of:
440+ (a) FA mask stride/tiling issues on ISWA's mixed SWA/FULL attention pattern,
441+ (b) fp16 precision accumulation during the verification batch, and
442+ (c) the unique batch shapes of speculative verification.
443+
444+ Gemma 4 has a mix of SWA and FULL attention layers (` swa=4 full=2 ` ). The bug
445+ is consistent with known upstream issues:
446+ - Vulkan FA mask stride fixes (#12720 , #12853 ) addressed mask loads but the
447+ underlying shader may still miscompute for ISWA + draft-verification pattern
448+ - Vulkan + Gemma 4 fp16 MMQ precision bug (ollama #15261 , fixed in ollama
449+ v0.30.0-rc31) — Vulkan explicitly produces wrong answers for Gemma 4 under
450+ certain conditions
451+ - The bug is specific to the FA kernel path during DFlash verification
452+ (normal decoding with FA is correct)
453+
454+ This is a ** Vulkan backend limitation** , not a DFlash logic bug. On CUDA the
455+ issue does not reproduce. The bug may also affect other ISWA models (Gemma 3,
456+ Ministral 3) under similar conditions.
457+
458+ ** Workaround** : Use ` --flash-attn off ` for Gemma 4 DFlash on Vulkan. With FA
459+ off, DFlash produces coherent output at ~ 17-19 tok/s (ring=0) — still ~ 50%
460+ faster than baseline. Stochastic sampling (`--temp 1.0 --top-k 64 --top-p
461+ 0.95`) is also less sensitive to the precision issue.
462+
463+ ### GLM second-opinion review
464+
465+ An independent review (glm-5.2: cloud via Ollama) confirmed:
466+ - The A/B evidence ** strongly supports** the interaction between DFlash and FA
467+ as the trigger
468+ - The workaround is ** highly sound** (FA is a perf optimization, not required
469+ for correctness)
470+ - The root cause statement was broadened from "ISWA mask corruption" to
471+ "Vulkan FA precision/mask handling during DFlash verification batches" to
472+ account for fp16 accumulation and batch-shape irregularities
473+ - The bug may affect other ISWA models (Gemma 3, Ministral 3) and should be
474+ tested if those models are used with Vulkan DFlash
475+
476+ ### 256K context stress test
477+
478+ At ` -c 262144 ` (256K context) with the hard prompt (~ 665 tokens, 512 generated):
479+ - All 5 models fit at 256K, zero crashes, zero OOM
480+ - Gemma 4 ring=0 (FA off): coherent, 10.49 tok/s, acc 0.215
481+ - Gemma 4 ring=1 stochastic: 12.37 tok/s, acc 0.564 (above baseline)
482+ - Correctness (FA off): 131/200 unique words (66%), matching baseline quality
483+
484+ | Model (256K) | Baseline gen | DFlash ring=0 | DFlash ring=1 | ring=1 stochastic | Result |
485+ | ---| ---| ---| ---| ---| ---|
486+ | Qwen3.6-27B | 12.36 tok/s | 21.72 (acc .746) | 21.42 (acc .743) | 14.55 (acc .434) | ✅ Best DFlash win (1.73×) |
487+ | 35B-A3B | 42.80 tok/s | 54.39 (acc .705) | 54.97 (acc .710) | 38.52 (acc .444) | ✅ Strong DFlash win (1.28×) |
488+ | Coder-Next | 53.22 tok/s | 30.25 (acc .328) | 46.68 (acc .478) | 45.29 (acc .284) | ✅ Stable, slower than baseline |
489+ | Gemma 4 31B | 11.33 tok/s | 10.49 (acc .215) | 10.83 (acc .185) | 12.37 (acc .564) | ✅ Stable; stochastic above baseline |
490+ | 122B-A10B | 19.04 tok/s | 9.67 (acc .144) | 13.22 (acc .146) | 16.23 (acc .164) | ✅ Stable, slower than baseline |
454491
455492## Ruled-out or deprioritized hypotheses
456493
@@ -570,6 +607,21 @@ const int output_len = n_draft + 1;
570607
571608and consumes only rows ` 1..output_len-1 ` .
572609
610+ ### Drafter KV slide without GPU cross-ring (commit ` 5b0e7533c ` )
611+
612+ Drafter KV cache is now slid correctly even without the GPU cross-ring, fixing
613+ the "drafter decode failed with 1" errors at 256K context. This is verified by
614+ the 256K regression test: all 20 runs (5 models × 4 configs) pass with zero
615+ crashes, zero OOM, and zero "failed to slide" warnings.
616+
617+ ### Regression unit tests (commit ` da8806748 ` )
618+
619+ Five new test functions in ` tests/test-dflash-plumbing.cpp ` guard all PR
620+ behavior changes. Each test was confirmed to FAIL when its corresponding fix is
621+ reverted. Production refactor: extracted ` build_recurrent_attn ` 's RS write-back
622+ slot decision into ` llama_dflash_rs_writeback_slot_for_test() ` in
623+ ` llama-context.h ` (minimal, behavior-identical).
624+
573625## Diagnostics retained
574626
575627The following environment variables are useful for future Vulkan / Qwen3Next /
@@ -595,15 +647,41 @@ requests and the snapshot slot index selected.
595647
596648The Vulkan path enables DFlash across the model set by:
597649
598- - Coder-Next: making the drafter graph choose the correct target-context source
599- (GPU-ring → drafter KV cache; Vulkan/CPU-hidden → fresh ` target_hidden `
600- projection), advancing Qwen3Next recurrent state during rollback, and keeping
601- the full query block.
602- - 122B: keeping ` n_rs_seq ` for QWEN35MOE so the GPU cross-ring can roll back
603- DeltaNet recurrent state without desyncing.
604- - 35B-A3B: rotating DeltaNet RS snapshots for ` n_seq_tokens < K ` so stochastic
605- sampling (which drives spec depth to 0 and turns every step into a decode)
606- does not clobber the snapshot history.
650+ - ** Coder-Next** : making the drafter graph choose the correct target-context
651+ source (GPU-ring → drafter KV cache; Vulkan/CPU-hidden → fresh
652+ ` target_hidden ` projection), advancing Qwen3Next recurrent state during
653+ rollback, and keeping the full query block.
654+ - ** 122B** : keeping ` n_rs_seq ` for QWEN35MOE so the GPU cross-ring can roll
655+ back DeltaNet recurrent state without desyncing.
656+ - ** 35B-A3B** : rotating DeltaNet RS snapshots for ` n_seq_tokens < K ` so
657+ stochastic sampling (which drives spec depth to 0 and turns every step into a
658+ decode) does not clobber the snapshot history.
659+ - ** Gemma 4** : Vulkan FA kernel corruption during DFlash verification for ISWA
660+ layers documented; ` --flash-attn off ` workaround produces coherent output at
661+ ~ 50% speedup over baseline.
662+
663+ ### PR head history
664+
665+ | Commit | Description |
666+ | ---| ---|
667+ | ` b788b4af1 ` | Original PR: dflash enable Qwen3-Coder-Next on Vulkan |
668+ | ` 648c0e9d6 ` | Original head: validate reduced-verify argmax on Vulkan |
669+ | ` bcd42973f ` | Sync Vulkan DFlash code from ` vulkan-dflash-cross-ring ` |
670+ | ` ab2be17fd ` | Keep ` n_rs_seq ` for QWEN35MOE (fix 122B GPU cross-ring corruption) |
671+ | ` efb07137c ` | Rotate DeltaNet RS snapshots for ` n_seq_tokens < K ` (fix 35B stochastic) |
672+ | ` da8806748 ` | Regression unit tests + RS write-back slot helper extraction |
673+ | ` 5b0e7533c ` | Slide drafter KV without GPU cross-ring (fix 256K decode fails) |
674+ | ` d6531e021 ` | Document Gemma 4 Vulkan DFlash FA corruption root cause and workaround |
675+
676+ ### Verified across
677+
678+ - ** 5 models** × ** 4 configs** (baseline, r0, r1, r1 stochastic) at both 4K and
679+ 256K context: all coherent, zero crashes, zero OOM
680+ - ** Unit tests** : ` test-dflash-plumbing.cpp ` passes on PR head; each new test
681+ confirmed to FAIL when its fix is reverted
682+ - ** Build** : Vulkan Release, 0 errors, 50 benign warnings (no -Werror), all
683+ binaries produced
684+ - ** 256K stress** : all 20 runs pass, zero "drafter decode failed with 1" errors
607685
608686Remaining low acceptance on out-of-distribution prompts is a drafter
609687training/generalization limitation, tracked separately from the Vulkan
0 commit comments