Skip to content

perf(speculative): commit per-row accepts in batched DFlash - #2865

Merged
inureyes merged 2 commits into
mainfrom
feature/issue-2442-dflash-per-row-accepts
Nov 6, 2026
Merged

inureyes merged 2 commits into
mainfrom
feature/issue-2442-dflash-per-row-accepts

Conversation

@inureyes

@inureyes inureyes commented Nov 6, 2026

Copy link
Copy Markdown
Member

Summary

The batched Qwen 3.5 DFlash round loop now commits a different accept count in every row instead of clamping the round to the shortest live row (issue #2353). The attention KV caches are compacted back to per-row contiguous valid prefixes, the verify block rotates and attends at each row's logical positions with its stale gap masked, and each GDN layer replays its state over that row's accepted prefix. The clamp stays reachable with MLXCEL_DFLASH_PER_ROW_ACCEPTS=0 and is selected automatically for quantized, paged or head-trimmed caches.

Measured at B = 4

M3 Ultra, qwen3.5-4b-4bit with qwen3.5-4b-dflash, block 16, 128 tokens per row, three alternating runs per arm.

arm aggregate tok/s rounds committed accepts per row-round walk
clamp 83.06 96 0.323 2.938
per-row 116.05 67 1.036 1.036

Per-row is 1.397x the clamp; B = 1 measured 78.09 tok/s. The drafter context is still a shared rectangular prefix, so the walk itself is lower than the clamp arm's. That limitation and the follow-up are recorded in docs/benchmark_results/dflash-per-row-accepts-m3ultra-2026-11-06.md.

Test plan

  • cargo test --profile test-fast --features metal,accelerate --test speculative_parity qwen35_dflash_batched -- --ignored --test-threads=1 (2 passed with per-row accepts as the default)
  • throughput_dflash_qwen35_batched_clamp_vs_per_row, ignored harness, three alternating runs per arm
  • cargo test --profile test-fast --lib models::qwen3_5_tests and the mlxcel-core round-loop unit tests
  • cargo clippy --lib --tests --features metal,accelerate -- -D warnings and cargo fmt --all -- --check

Closes #2442

@inureyes inureyes added status:done Completed type:performance Performance improvements priority:medium Medium priority area:inference Generation, sampling, decoding (incl. speculative, DRY) labels Nov 6, 2026
inureyes added a commit that referenced this pull request Nov 6, 2026
Add the bilingual technical report for PR #2865, which commits per-row accepts in the batched DFlash round loop.

Records the B = 4 measurement (83.06 to 116.05 tok/s, 1.397x), the accept and round counts, the parity gate, the measurement caveat, and the unvalidated surface.
The batched Qwen 3.5 DFlash round loop clamped every round to the shortest live row because the attention KV cache was rectangular. Committed accepts sat at 0.323 per row-round against a 2.938 walk, so batching bought only 1.06x over B = 1.

Each row now commits its own walk count. The target compacts its attention KV back to per-row contiguous valid prefixes, rotates and masks the verify block at each row's logical positions with a per-query stale-gap mask, and replays each GDN layer's conv and recurrent state over that row's own accepted prefix. Dense Fp16 caches take the per-row path; a quantized, paged or head-trimmed cache falls back to the clamp, and MLXCEL_DFLASH_PER_ROW_ACCEPTS=0 restores it explicitly.

Measured on an M3 Ultra, qwen3.5-4b-4bit target with qwen3.5-4b-dflash, block 16, 128 tokens per row, three alternating runs per arm: B = 4 aggregate 116.05 tok/s against the clamp's 83.06 (1.397x) and B = 1's 78.09; committed accepts 1.036 against 0.323 per row-round. The drafter context stays a shared rectangular prefix, so the walk itself falls from 2.938 to 1.036; per-row drafter context is a separate follow-up.

Validation: the qwen35_dflash_batched parity gate and its identical-prompts control pass with per-row accepts as the default under the #2851 near-tie margin, the attention-compaction unit tests pass, and cargo fmt plus cargo clippy --lib --tests -D warnings are clean. Numbers and method: docs/benchmark_results/dflash-per-row-accepts-m3ultra-2026-11-06.md.

Refs #2442
Add the bilingual technical report for PR #2865, which commits per-row accepts in the batched DFlash round loop.

Records the B = 4 measurement (83.06 to 116.05 tok/s, 1.397x), the accept and round counts, the parity gate, the measurement caveat, and the unvalidated surface.
@inureyes
inureyes force-pushed the feature/issue-2442-dflash-per-row-accepts branch from d87e5f0 to 0be2d26 Compare November 6, 2026 01:01
@inureyes
inureyes marked this pull request as ready for review November 6, 2026 01:03
@inureyes
inureyes merged commit 8316eb2 into main Nov 6, 2026
32 checks passed
@inureyes
inureyes deleted the feature/issue-2442-dflash-per-row-accepts branch November 6, 2026 01:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:inference Generation, sampling, decoding (incl. speculative, DRY) priority:medium Medium priority status:done Completed type:performance Performance improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(speculative): close batched DFlash gaps left by the uniform clamp

1 participant