Current state
Qwen3 speculative decoding (DFlash) is greedy-only. Acceptance is argmax equality, not distribution-correct rejection sampling:
accept_greedy in openinfer-qwen3/src/speculative.rs compares draft tokens against the target model's argmax at each position. The module doc explicitly notes: "Sampling-correct acceptance would need the target model's full distribution … it is left until a sampling method actually lands."
- The executor hard-asserts greedy in
openinfer-qwen3/src/executor/spec.rs (both draft and verify paths): speculative verification currently supports greedy sampling only.
- The scheduler gates the speculative path via
should_speculative_decode in openinfer-qwen3/src/scheduler/plan.rs, requiring params.is_greedy() for every active request. A single sampled request silently drops the whole batch back to plain decode.
Net effect: any request with temperature > 0 gets zero speedup from speculative decoding.
What's needed
Standard rejection sampling (Leviathan et al. 2023 / vLLM-style):
- Verify forward must expose the target distribution (post-temperature/top-p) at each of the
K + 1 positions, not just argmax.
- Draft side must record its proposal probabilities
q(x) per proposed token.
- Acceptance: accept draft token
x with probability min(1, p(x) / q(x)); on rejection, resample from the normalized residual max(0, p − q) and stop.
- Bonus token at full acceptance is sampled from the target distribution at position
K + 1.
- Relax the
is_greedy() gates in the scheduler and executor; keep per-request SamplingParams respected within a batch.
Correctness bar: the output distribution must be provably identical to non-speculative sampling (the rejection-sampling guarantee), gated by a statistical test against plain decode at matching seeds/params.
Notes
- Batch-level all-or-nothing gating in
should_speculative_decode could also be revisited (mixed greedy/sampled batches), but that's a follow-up — per-request rejection sampling is the prerequisite.
Current state
Qwen3 speculative decoding (DFlash) is greedy-only. Acceptance is argmax equality, not distribution-correct rejection sampling:
accept_greedyinopeninfer-qwen3/src/speculative.rscompares draft tokens against the target model's argmax at each position. The module doc explicitly notes: "Sampling-correct acceptance would need the target model's full distribution … it is left until a sampling method actually lands."openinfer-qwen3/src/executor/spec.rs(both draft and verify paths):speculative verification currently supports greedy sampling only.should_speculative_decodeinopeninfer-qwen3/src/scheduler/plan.rs, requiringparams.is_greedy()for every active request. A single sampled request silently drops the whole batch back to plain decode.Net effect: any request with
temperature > 0gets zero speedup from speculative decoding.What's needed
Standard rejection sampling (Leviathan et al. 2023 / vLLM-style):
K + 1positions, not just argmax.q(x)per proposed token.xwith probabilitymin(1, p(x) / q(x)); on rejection, resample from the normalized residualmax(0, p − q)and stop.K + 1.is_greedy()gates in the scheduler and executor; keep per-requestSamplingParamsrespected within a batch.Correctness bar: the output distribution must be provably identical to non-speculative sampling (the rejection-sampling guarantee), gated by a statistical test against plain decode at matching seeds/params.
Notes
should_speculative_decodecould also be revisited (mixed greedy/sampled batches), but that's a follow-up — per-request rejection sampling is the prerequisite.