Skip to content

diffusiongemma: first real-26B Vulkan generation — quantized Q5_0/Q8_0 down banks + UMA heap fallback (#40, #120) - #122

Merged
jamesburton merged 6 commits into
devfrom
issue/40-vulkan-real-26b
Jul 3, 2026
Merged

jamesburton merged 6 commits into
devfrom
issue/40-vulkan-real-26b

Conversation

@jamesburton

Copy link
Copy Markdown
Owner

First real-weight DiffusionGemma-26B runs on the Vulkan backend, plus the fixes that made them possible. Closes #120.

What happened

  • DiffusionGemmaVulkanRealGenerationTests — env-gated (DOTLLM_DIFFUSIONGEMMA_GGUF + Vulkan device) end-to-end masked-diffusion generation on the real 26B GGUF, reporting denoise-steps/sec, canvas latency and effective tok/s (DOTLLM_DG_CANVAS/STEPS/PROMPT/PKV overridable). Passes on Strix Halo: 15 steps / 58.6 s at canvas 32, SC+PKV on, coherent factual completion.
  • Vulkan gemma4 MoE: real conversions load quantized now (Vulkan gemma4 MoE: real 26B Q4_K_M OOMs - Q5_0/Q8_0 down-expert banks force F32 fallback; no UMA heap fallback in allocator #120). The unsloth Q4_K_M ships ffn_down_exps as Q5_0 ×16 + Q8_0 ×14 (never Q5_1) — all 30 layers previously fell into the F32 host-dequant fallback (~92 GB → OOM). Now: Q5_0 downs repack bit-exactly to Q5_1 at upload (d·(q−16) = d·q + m, m = −16d, fp16-exact — unit-tested) and ride the validated Q5_1 scale-fold shader; Q8_0 downs keep the existing scale-less Q8_0 indexed kernel with ffn_down_exps.scale[e] folded into each block's fp16 d.
  • Heap-aware device-local OOM fallback. gfx1151's strict DEVICE_LOCAL heap is 15.82 GiB (smaller than the model); heap[1] is 96 GiB DEVICE_LOCAL|HOST_VISIBLE UMA. On VK_ERROR_OUT_OF_DEVICE_MEMORY, AllocateInternal now retries every eligible type — other heaps before the failed heap, larger heaps first, combined rung before plain host-visible (llama.cpp GGML_VK_ALLOW_SYSMEM_FALLBACK equivalent). Opt-out DOTLLM_VULKAN_STRICT_DEVICE_LOCAL=1; occurrences surfaced via VulkanDevice.DeviceLocalFallbackCount.
  • Docs: REAL-26B-RESULTS.md (CPU + Vulkan generation results, throughput matrix vs llama.cpp), SUPERSEDED header on the stale GEMMA4-GPU-GAPS.md, stale #36 guard remark fixed in DiffusionGemmaConfigExtractor.

Measured (Strix Halo, same session, GPU idle-checked)

dotLLM Vulkan llama.cpp Vulkan ratio
AR decode, LLaDA-8B Q4_K_M (same file) 28.24 tok/s 44.85 tok/s 0.63×
26B diffusion step 3.90 s @ canvas 32 0.736 s @ canvas 256 ~36×/canvas-token

The diffusion gap is the dense SC soft-embed (~47 GFLOP/step at canvas 32) + host-side sampling — follow-up filed as #121 (also covers the canvas-256 VK_ERROR_DEVICE_LOST TDR from monolithic dispatches).

Tests

  • VulkanWeightsQuantRepackTests (CPU-only): Q5_0→Q5_1 dequant bit-exactness incl. ±0/subnormal scales; Q8_0 scale-fold payload/rounding invariants. Green.
  • Gemma4DiffusionVulkanTests + Gemma4VulkanKvCacheTests (GPU, synthetic): green on gfx1151.
  • Real-weight: CPU DiffusionGemmaGgufForwardTests 3/3 green; Vulkan generation test green at canvas 32.

🤖 Generated with Claude Code

jamesburton and others added 6 commits July 3, 2026 17:41
#40)

Env-gated (DOTLLM_DIFFUSIONGEMMA_GGUF + Vulkan device) denoise-loop generation
on the real 26B GGUF via VulkanTransformerModel + DiffusionTextGenerator.
Reports denoise-steps/sec, per-step canvas latency and effective tok/s
(canvas/steps/prompt/PKV overridable via DOTLLM_DG_*).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ment (#40)

GEMMA4-GPU-GAPS.md gets a SUPERSEDED header (the Vulkan/CUDA gemma4 gaps it
scopes were closed the same overnight, merged via 41621d5); the
DiffusionGemmaConfigExtractor remark no longer claims the removed
ValidateGemma4UniformHeadDim guard blocks real-weight forward.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… fallback (#120, #40)

Real unsloth Q4_K_M conversions ship ffn_down_exps as Q5_0/Q8_0 (never Q5_1),
so every layer fell back to the F32 host-dequant path (~92 GB) and OOMed the
15.8 GiB strict device-local heap on Strix Halo.

- Gemma4ExpertsKeepQuantized now accepts down in {Q5_1, Q5_0, Q8_0}.
- Q5_0 downs repack bit-exactly to Q5_1 at upload (d*(q-16) = d*q + m with
  m = -16d, fp16-exact) and ride the validated Q5_1 scale-fold shader.
- Q8_0 downs stay Q8_0 with ffn_down_exps.scale[e] folded into each block
  fp16 d; dispatched by the existing scale-less Q8_0 indexed kernel.
- AllocateDeviceLocal: on VK_ERROR_OUT_OF_DEVICE_MEMORY retry the combined
  DEVICE_LOCAL|HOST_VISIBLE type (UMA/GTT heap), then host-visible
  (llama.cpp GGML_VK_ALLOW_SYSMEM_FALLBACK equivalent); opt-out via
  DOTLLM_VULKAN_STRICT_DEVICE_LOCAL=1; DeviceLocalFallbackCount reports use.
- CPU unit tests: Q5_0->Q5_1 bit-exactness, Q8_0 scale-fold payload/rounding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The first ladder picked the FIRST type matching the fallback flags, which on
AMD APUs can share the exhausted carve-out heap - the retry then re-failed
(canvas-256 real run OOMed in UploadMatrix despite the fallback). Enumerate
all eligible types per rung instead: other heaps before the failed heap,
larger heaps first.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oughput matrix (#40, #41, #33)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…thic dispatches -> #121 (#40)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables real-weight DiffusionGemma-26B end-to-end generation on the Vulkan backend by keeping Gemma-4 MoE down-expert banks quantized for real conversions (Q5_0/Q8_0) and improving Vulkan device-local allocation robustness via heap-aware fallback, with accompanying tests and measurement docs.

Changes:

  • Keep Gemma-4 MoE experts quantized for real-world ffn_down_exps types by repacking Q5_0→Q5_1 at upload and folding per-expert scale into Q8_0 block scales.
  • Add heap-aware Vulkan VK_ERROR_OUT_OF_DEVICE_MEMORY fallback across eligible memory types/heaps (opt-out via env var) and expose a fallback counter for harness reporting.
  • Add CPU-only exactness tests for the repack logic, an env-gated real-weight Vulkan generation integration test, and new/updated diffusiongemma documentation.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/DotLLM.Tests.Unit/Vulkan/VulkanWeightsQuantRepackTests.cs Adds CPU-only tests validating Q5_0→Q5_1 repack and Q8_0 scale folding.
tests/DotLLM.Tests.Integration/Backends/DiffusionGemmaVulkanRealGenerationTests.cs Adds env-gated real-weight DiffusionGemma-26B Vulkan end-to-end generation harness.
src/DotLLM.Vulkan/VulkanWeights.cs Implements Q5_0→Q5_1 repack and Q8_0 scale-fold upload paths for MoE down banks; widens quantized eligibility.
src/DotLLM.Vulkan/VulkanDevice.cs Adds heap-aware device-local OOM fallback logic and a counter for fallback occurrences.
src/DotLLM.Models/SafeTensors/DiffusionGemmaConfigExtractor.cs Updates remarks documenting per-layer head_dim support (#36).
docs/diffusiongemma/REAL-26B-RESULTS.md Adds real-weight CPU/Vulkan measurement notes and results matrix.
docs/diffusiongemma/GEMMA4-GPU-GAPS.md Marks historical gap report as superseded and adds context pointers.

Half scale = *(Half*)s;
*(Half*)d = scale;
*(Half*)(d + 2) = (Half)(-16f * (float)scale);
new ReadOnlySpan<byte>(s + 2, 20).CopyTo(new Span<byte>(d + 4, 20)); // qh + qs verbatim
for (long b = 0; b < blockCount; b++)
{
*(Half*)d = (Half)((float)*(Half*)s * scale);
new ReadOnlySpan<byte>(s + 2, 32).CopyTo(new Span<byte>(d + 2, 32));
Comment on lines +59 to +63
// Bit-exact: d·q and 16·d are both exact in F32 (11-bit × 5-bit significands),
// so d·q − 16d and d·(q−16) round identically.
for (int i = 0; i < elems; i++)
Assert.True(fromQ5_0[i].Equals(fromQ5_1[i]),
$"elem {i}: Q5_0={fromQ5_0[i]:G9} != converted Q5_1={fromQ5_1[i]:G9}");
Comment on lines +107 to +110
Half foldedD = MemoryMarshal.Read<Half>(dst.AsSpan(b * Q8_0BlockBytes));
int q = (sbyte)src[b * Q8_0BlockBytes + 2 + (i % GroupSize)];
Assert.True(((float)foldedD * q).Equals(scaled[i]),
$"elem {i}: expected {(float)foldedD * q:G9}, got {scaled[i]:G9}");
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.

2 participants