Skip to content

dspark: fold first-token forward into verify + bit-exact M5 Metal decode wins - #778

Open
polymorf wants to merge 3 commits into
antirez:mainfrom
polymorf:metal-dspark-fold
Open

dspark: fold first-token forward into verify + bit-exact M5 Metal decode wins#778
polymorf wants to merge 3 commits into
antirez:mainfrom
polymorf:metal-dspark-fold

Conversation

@polymorf

Copy link
Copy Markdown

Three commits, measured on M5 Max 40-core / 128 GB with the ds4f-q2 model. The default (non-speculative) decode path stays bit-exact throughout: every change was gated by an abort-on-diff A/B harness comparing full-vocabulary logit frontiers, and the official bench keeps the canonical frontier hash (prefill 786-788 t/s, steady decode 45.50-45.58 t/s).

dspark: fold the first token's forward into the verify batch

With --dspark, each accept/miss cycle previously paid a standalone target forward for the first token before the verify batch. This folds that forward into the verify dispatch as row 0 (the DFlash protocol shape), so one batched pass covers first token + verification. The fold is self-regulating: it re-arms only on a full accept; any partial accept or miss falls back to the standalone path, which resynchronizes draft conditioning.

Also in this commit, all opt-in via env:

  • DS4_DSPARK_CONFIDENCE_GRID — per-position draft confidence thresholds (best: 0.55,0.6,0.65)
  • DS4_DSPARK_SCHEDULER_BACKOFF — adaptive selectivity + regime pauses on consecutive miss streaks
  • DS4_DSPARK_MARGIN_GATE — skip proposing when the target's top1-top2 logit margin says it is hesitating (the draft has no chance there); applied at the standalone site only
  • r1 6/7/8 row-count instantiations for the ext matvec (the 6-row verify blocks sat on a 2x dense-relu cliff)

With DS4_DSPARK_FOLD=1 DS4_DSPARK_CONFIDENCE_GRID=0.55,0.6,0.65 DS4_DSPARK_SCHEDULER_BACKOFF=1 DS4_DSPARK_MARGIN_GATE=1 (temp 0, -n 512), against plain decode on the same channel:

channel plain dspark delta
self-similar payload 46.2 t/s 62.8 t/s +36%
codegen (red-black tree) 46.8 t/s 48.2 t/s +2.9%
prose 44.8 t/s 46.6 t/s +4.0%

No channel regresses — the margin gate is what turned prose from a loss into a gain (the speculative path only works its genuinely predictable spans, at ~87% acceptance). The speculative flow is deterministic run-to-run.

metal: fix staged B-tile tensor extents in the mm_id mpp kernel

Latent-bug fix, also submitted standalone as #777 (kept as its own commit here so either merge order works): the tB extents were declared transposed and only accidentally correct because NR1 == NK == 32. Bit-identical today; required for any future NR1 != NK instantiation.

metal: two M5 decode encoder/occupancy wins, both bit-exact

  • HC producer spread: one live NR0=2 cluster per comb threadgroup instead of packing both clusters on groups 2..5, raising the producer grid from 6 to 10 threadgroups. Identical per-row math, bit-identical outputs; ~+0.12% decode on M5 Max. Default on M5-class GPUs, kill switch DS4_METAL_DISABLE_M5_HC_SPREAD.
  • FFN island into HC expand: publish the parallel-FFN join outputs with a memory barrier inside the still-open concurrent encoder so the HC expansion dispatch lands there too, saving one encoder transition per layer. Kill switch DS4_METAL_DISABLE_M5_FFN_ISLAND_INTO_HC.

make test passes, --metal-kernels green, ROCm/CUDA paths untouched apart from the already-merged DSpark support hooks.

🤖 Generated with Claude Code

polymorf and others added 3 commits August 11, 2026 14:18
Metal DSpark decode reworked around the DFlash block layout and a set of
regime signals, all opt-in and deterministic at temp 0:

- DS4_DSPARK_FOLD=1 verifies the committed token as batch row zero instead
  of paying a standalone one-token forward per cycle (~22 ms). The existing
  accept loop, prefix slots, replay fallback and hidden capture apply to the
  folded block unchanged. Folding re-arms only after a full accept; any miss
  or partial drops back to the standalone path, which refreshes the draft
  conditioning and returns control to the scheduler.
- DS4_DSPARK_CONFIDENCE_GRID=a,b,c grades the confidence pruning threshold
  by draft position (stale-conditioned folds over-prune depth with the flat
  default; 0.55,0.6,0.65 measured optimal under fold).
- DS4_DSPARK_SCHEDULER_BACKOFF=1 doubles pause lengths per consecutive
  unproductive cycle (bounded x8) and raises the position-0 engagement bar
  (+0.05/step, bounded +0.20); one accepted draft resets both.
- DS4_DSPARK_MARGIN_GATE=<logits> skips standalone proposing when the
  target's own top1-top2 logit margin is below the bar: a hesitant target
  is content the weaker draft cannot predict, and the margin is free on the
  host. Fold-chain proposing is never gated, a full accept just validated
  the chain.
- The per-row batch compressor update loops take the packed ratio-4 pool
  (bit-identical per the in-tree pack gate), and a fused sequential rows
  kernel with bit-exact tests covers large non-aligned chunks (n_tokens
  >= 16; small verify batches measured faster unfused).
- mv_ext gains r1_6..8 row tiles so 6-8 row verify blocks stop reading
  every dense weight twice; the speculative head keeps its eight-row
  padding on CUDA only. A Metal port of the CUDA fused markov argmax ships
  behind DS4_DSPARK_GPU_MARKOV (per-position submit latency outweighs the
  CPU matvec it replaces on Apple; kept as the building block for a
  batched chain). DS4_METAL_GAP_PROFILE reports GPU-timeline holes at
  token boundaries. speed-bench/ngram_similarity.py characterizes workload
  self-similarity for speculation triage.

With --dspark --mtp <support.gguf> plus FOLD, GRID=0.55,0.6,0.65,
BACKOFF=1 and MARGIN_GATE=1, greedy CLI throughput on M5 Max vs plain
decode: structured payload 46.2 -> 62.8 t/s (+36%), codegen 46.8 -> 48.2
(+2.9%), Italian prose 44.8 -> 46.6 (+4.0%); no measured workload
regresses. The default bit-exact path is untouched: official bench
replicas keep the canonical frontier logits SHA-256
(eb794f497861d7d9e373665f6e115d8ebe4e17c13c431aa7e318282f16a0f21d) and
full make test plus the focused Metal kernel suites pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The staged B tile stores element (n, k) at sb[n*NK + k]: dim0 is K
(stride 1), dim1 is N (stride NK). The tB tensor declared its extents
as (NR1, NK), which only described the layout correctly because
NR1 == NK == 32; any future instantiation with NR1 != NK would compute
against a transposed view of the staged tile.

Spell the extents (NK, NR1). Extents and strides are identical while
NR1 == 32, so the shipped kernel is unchanged: verified bit-exact on
full-vocabulary logit frontiers, and the Metal kernel tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- HC producer spread: give every comb threadgroup one live NR0=2
  cluster instead of packing both clusters on groups 2..5, raising the
  producer grid from 6 to 10 threadgroups. Per-row lane traversal,
  reduce tree and epilogue are identical across both shapes, so the
  outputs are bit-identical; the wider grid measured ~+0.12% decode on
  M5 Max (40-core). Default on M5-class GPUs, kill switch
  DS4_METAL_DISABLE_M5_HC_SPREAD.

- FFN island into HC expand: at the parallel-FFN join, publish the
  routed/shared outputs with a memory barrier inside the still-open
  concurrent encoder and let the single HC expansion dispatch land in
  the same encoder, saving one encoder transition per layer. Kill
  switch DS4_METAL_DISABLE_M5_FFN_ISLAND_INTO_HC.

Both changes verified bit-exact against the previous defaults on
full-vocabulary logit frontiers (8 runs each, abort-on-diff harness);
official bench keeps the canonical frontier hash with prefill 786-788
t/s and steady decode 45.50-45.58 t/s on M5 Max.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant