Skip to content

perf(server): tick-cooperative speculative decoding to remove the burst head-of-line block #734

Description

@inureyes

Context

#638 part 2 asked to replace the run-to-completion speculative burst (src/server/batch/speculative_burst.rs, dispatched from execute_prefill) with tick-cooperative slices so a speculative request no longer stalls every concurrent classic decode row for its whole duration (HOL block documented at src/server/model_worker.rs:400-438). PR #733 landed the measurement side (pairing matrix, adaptive-policy de-rate so GB10 declines regressing pairings, and a BurstFinalized.burst_wall_ms observability counter that measures the HOL stall) but deferred the slice itself.

Blocker found (by #733)

MtpGenerator<T> borrows the model through its adapter, so the generator cannot be stashed across scheduler ticks (self-referential borrow); this is exactly why the burst runs to completion today. A resumable design needs a generator step API with stored per-round state (feasible: MtpVerifyOutput owns MLX handles, not model borrows) plus drafter re-seed on resume.

Design sketch (from #638's plan and #733's analysis)

One speculative round (draft K + verify) per scheduler tick, re-entering the tick loop between rounds (round_loop.rs / round_loop_batched.rs are already per-round structured); per-sequence KV slots preserve state across interleaved classic decodes; the cross-tick lookahead pipeline (#632) is already excluded under speculative dispatch (lookahead_params returns None), and any new action type must call discard_lookahead like the Prefill action does; preferred end state is speculative rounds as first-class decide_action outcomes.

Acceptance criteria

  • With one speculative and one classic request active, the classic request's inter-token latency stays bounded (scheduler-level regression test with a mock/small model, plus manual E2E via scripts/bench_serving_concurrency.py --concurrency 2).
  • burst_wall_ms drops to about one round length.
  • Speculative-vs-greedy output parity holds for B=1 and batched.
  • Env gate semantics unchanged.
  • cargo test --features cuda passes.

Refs: deferred from #638 / PR #733.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:inferenceGeneration, sampling, decoding (incl. speculative, DRY)priority:mediumMedium prioritystatus:readyReady to be worked ontype:performancePerformance improvements

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions