On Metal (M4 Max, Flash q2-q4, 0731 DSpark support GGUF, 6747e77), DSpark is consistently net-negative on generation:
| corpus |
baseline gen |
--dspark |
change |
| prose, 9485 tok |
29.31 t/s |
23.02 t/s |
-21.5% |
| prose, 28790 tok |
27.82 t/s |
23.10 t/s |
-17.0% |
| code, 9452 tok |
29.53 t/s |
22.90 t/s |
-22.4% |
Reproducible to 0.2% across reps; prefill is unaffected as expected.
This is not the ROCm no-draft failure in #597 — here DSpark proposes normally and its proposals are usually accepted. DS4_DSPARK_STATS=1, prose:
cycles=88 proposed=44 accepted_draft=31 accept_rate=70.45% avg_accept=0.352
full=14 partial=2 miss_first=7 no_draft=65 scheduler_skips=52
draft_len_hist=1:10,2:6,3:6,4:1 accepted_len_hist=0:72,1:6,2:5,3:5
time_ms propose=332.0 verify=964.4 replay=1063.1 spec_total=2039.9
saved=1045.3 net_saved=-1326.6
The economics: each accepted token saves ~34 ms (one baseline token at 29.3 t/s), and 31 were accepted, giving saved=1045 ms. But replay=1063 ms over 44 proposals is ~24 ms per speculation cycle — roughly one full token. So a cycle must accept more than one token just to pay for its own replay, while avg_accept is 0.35.
The scheduler is clearly trying to avoid this: 52 of 88 cycles were skipped, and the no-draft pause logic fires. But it still ends up net-negative, which suggests its break-even model may not include the replay term — the largest single cost in the breakdown.
Two observations that may help localise it:
- Code is worse than prose, contradicting the README guidance that predictable continuations benefit most: accept_rate 62.22% vs 70.45%, avg_accept 0.308 vs 0.352, 77/91 vs 72/88 cycles accepting zero. Corpus was a 9452-token prefix of
ds4.c itself, length-matched to the prose prompt within 0.3%.
- Drafts never exceed 4 tokens in either corpus, and
--mtp-draft has no effect across 1/4/8 (22.90 / 22.94 / 22.94 t/s) — expected, since that flag drives the legacy MTP path, but worth stating so others do not try it as a workaround.
Not asking for the replay fix (7fb2830) to be reverted — it is a correctness fix, and removing it would only move net_saved from -1327 ms to roughly -264 ms, still a loss. The suggestion is narrower: if the scheduler counted replay in its break-even estimate, it could pause speculation earlier on low-yield content and converge to baseline instead of below it.
Happy to run experiments if useful — the telemetry above is easy to regenerate.
On Metal (M4 Max, Flash q2-q4, 0731 DSpark support GGUF,
6747e77), DSpark is consistently net-negative on generation:--dsparkReproducible to 0.2% across reps; prefill is unaffected as expected.
This is not the ROCm no-draft failure in #597 — here DSpark proposes normally and its proposals are usually accepted.
DS4_DSPARK_STATS=1, prose:The economics: each accepted token saves ~34 ms (one baseline token at 29.3 t/s), and 31 were accepted, giving
saved=1045 ms. Butreplay=1063 msover 44 proposals is ~24 ms per speculation cycle — roughly one full token. So a cycle must accept more than one token just to pay for its own replay, whileavg_acceptis 0.35.The scheduler is clearly trying to avoid this: 52 of 88 cycles were skipped, and the no-draft pause logic fires. But it still ends up net-negative, which suggests its break-even model may not include the replay term — the largest single cost in the breakdown.
Two observations that may help localise it:
ds4.citself, length-matched to the prose prompt within 0.3%.--mtp-drafthas no effect across 1/4/8 (22.90 / 22.94 / 22.94 t/s) — expected, since that flag drives the legacy MTP path, but worth stating so others do not try it as a workaround.Not asking for the replay fix (
7fb2830) to be reverted — it is a correctness fix, and removing it would only movenet_savedfrom -1327 ms to roughly -264 ms, still a loss. The suggestion is narrower: if the scheduler counted replay in its break-even estimate, it could pause speculation earlier on low-yield content and converge to baseline instead of below it.Happy to run experiments if useful — the telemetry above is easy to regenerate.