Skip to content

Commit 2025391

Browse files
author
Gert Boddaert
committed
docs: update enable-and-fix-dflash-on-vulkan-various-models with latest status
- Added PR head history table (b788b4ad6531e0) - Updated Gemma 4 section: evidence table, broadened root cause (GLM review), 256K stress test results, GLM second-opinion findings - Added drafter KV slide fix (5b0e753) and unit tests (da88067) sections - Updated final status with verification matrix and PR head link - Updated summary table with current Gemma 4 status - Added Gemma 4 to post-fix performance matrix
1 parent a3ec40b commit 2025391

1 file changed

Lines changed: 125 additions & 47 deletions

File tree

docs/enable-and-fix-dflash-on-vulkan-various-models.md

Lines changed: 125 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@ Vulkan backend across the model set** tested on AMD Strix Halo (Radeon 8060S /
55
RADV GFX1151, Mesa 25.0.7, Vulkan 1.4.309, 96 GB UMA). It consolidates the
66
original 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

1011
It replaces the scattered investigation notes that were created while debugging
1112
the original 0% draft-token acceptance problem and the later qwen35moe
1213
recurrent-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

1620
DFlash 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

2832
The 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

7075
Interpretation:
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
418427
fully 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

571608
and 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

575627
The following environment variables are useful for future Vulkan / Qwen3Next /
@@ -595,15 +647,41 @@ requests and the snapshot slot index selected.
595647

596648
The 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

608686
Remaining low acceptance on out-of-distribution prompts is a drafter
609687
training/generalization limitation, tracked separately from the Vulkan

0 commit comments

Comments
 (0)