ruvllm: real speculative decoding + sparse attention integration#650
Draft
ricable wants to merge 1 commit into
Draft
ruvllm: real speculative decoding + sparse attention integration#650ricable wants to merge 1 commit into
ricable wants to merge 1 commit into
Conversation
…ruvllm speculative.rs previously faked draft/verify by round-tripping through generate()'s text API (detokenize -> retokenize per candidate token) instead of comparing real logits, and its perf claims were untested. This replaces it with a real, logit-level implementation: - Vendor+patch candle-transformers 0.9.2 (patches/candle-transformers, following the existing patches/hnsw_rs convention) to add ModelWeights::forward_all_positions, returning per-position logits from a multi-token forward pass instead of only the last position — needed to verify K draft tokens against the main model in a single batched forward pass, the entire point of speculative decoding. Also fixes a latent causal mask bug (square [t,t] mask fails to broadcast when continuing a non-empty KV cache with more than one new token — a combination nothing in this codebase exercised before) and adds cheap KV-cache snapshot/restore so a rejected draft token can be rolled back without a full context replay. - New SpeculativeBackend trait (implemented for CandleBackend) exposing forward_logits/reset_context/snapshot_context/restore_context; rewrite SpeculativeDecoder's draft/verify/generate_tokens around it with greedy argmax-match verification and real log-probabilities. - examples/speculative_bench.rs: end-to-end benchmark on real GGUF weights (Llama-2-7B main + TinyLlama-1.1B draft, same tokenizer). Measured results are acceptance-rate dependent as expected from the literature: ~1.1x at 85.9% acceptance, ~0.6-0.7x at 62-70% acceptance where per-round forward-call overhead isn't fully amortized. Documented in speculative.rs. Also wires crates/ruvllm_sparse_attention into the inference path instead of leaving it standalone: a new `sparse-attention` feature routes GGUF prompt-prefill attention through SubquadraticSparseAttention (via forward_sparse/forward_attn_sparse in the same patched quantized_llama.rs), falling back to dense attention for incremental decode steps where forward_gqa's q.seq==k.seq contract doesn't hold. CandleBackend gains enable_sparse_attention/disable_sparse_attention/sparse_attention_enabled. examples/sparse_attention_check.rs verifies the tensor bridging end-to-end on real weights (coherent, non-garbled output on both short and window-exceeding prompts). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Speculative decoding was previously a stub:
speculative.rs's draft/verify phases round-tripped throughgenerate()'s text API (detokenize → retokenize per candidate token) instead of comparing real logits — no actual batched verification, no real speedup mechanism. This replaces it with a real, logit-level implementation:candle-transformers0.9.2 (patches/candle-transformers, following the existingpatches/hnsw_rsconvention) to addModelWeights::forward_all_positions— per-position logits from a multi-token forward pass, needed to verify K draft tokens against the main model in a single batched forward pass. Also fixes a latent causal-mask bug (a square[t,t]mask fails to broadcast when continuing a non-empty KV cache with more than one new token — a combination nothing in this codebase exercised beforeforward_all_positionsexisted), and adds cheap KV-cache snapshot/restore so a rejected draft token rolls back without a full context replay.SpeculativeBackendtrait (implemented forCandleBackend) exposingforward_logits/reset_context/snapshot_context/restore_context;SpeculativeDecoder's draft/verify/generate_tokens rewritten around it with greedy argmax-match verification and real log-probabilities.examples/speculative_bench.rs: end-to-end benchmark on real GGUF weights (Llama-2-7B main + TinyLlama-1.1B draft, same tokenizer — required, see module docs). Measured results are acceptance-rate dependent, as expected from the literature: ~1.1x speedup at 85.9% draft acceptance, ~0.6-0.7x at 62-70% acceptance where per-round forward-call overhead isn't fully amortized. Documented honestly inspeculative.rs's module docs — not every prompt shows a win, and that's a property of the technique, not a bug.ruvllm_sparse_attentionwas declared but unused — wired it into the actual inference path instead of leaving it standalone: a newsparse-attentionfeature routes GGUF prompt-prefill attention throughSubquadraticSparseAttention(forward_sparse/forward_attn_sparsein the same patchedquantized_llama.rs), falling back to dense attention for incremental decode steps whereforward_gqa'sq.seq == k.seqcontract doesn't hold.CandleBackendgainsenable_sparse_attention/disable_sparse_attention/sparse_attention_enabled.examples/sparse_attention_check.rsverifies the tensor bridging end-to-end on real weights.Test plan
cargo build -p ruvllm --features candle,metal— clean (regression check, sparse-attention off)cargo build -p ruvllm --features candle,metal,sparse-attention— cleancargo test -p ruvllm --features candle,metal,sparse-attention --lib— 1589 passed, 2 ignoredcargo test -p ruvllm --features candle,metal,sparse-attention --test speculative_integration— 29 passedexamples/speculative_bench.rsrun end-to-end against real downloaded GGUF weights (Llama-2-7B + TinyLlama-1.1B) — real measured tok/s, not simulatedexamples/sparse_attention_check.rsrun end-to-end — coherent (non-garbled) output on both short and window-exceeding prompts, dense vs sparse🤖 Generated with Claude Code