Skip to content

DSpark scheduler break-even model appears not to account for replay cost (Metal, net -1327ms despite 70% accept rate) #695

Description

@datanerdie

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:

  1. 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%.
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions