Repository navigation
llama : flag the outputs of every backend sampling chain - #335
danielhanchen wants to merge 1 commit into
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b240c56069
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // flag the outputs of every chain, active or not: the graph keeps its topology across ubatches, so the | ||
| // allocator can reuse the tensor addresses of a graph built for a different set of active chains, and an | ||
| // output that was an intermediate there (freed and overwritten by the next chain) would alias here |
There was a problem hiding this comment.
Remove the hard wrapping from the new comment
The new comment splits one sentence across three fixed-width lines, contrary to the repository's explicit rule in AGENTS.md: "Do NOT split a line into multiple lines mid-sentence". Keep the allocator-lifetime explanation, but shorten it or leave the sentence unwrapped to follow the documented readability convention.
AGENTS.md reference: AGENTS.md:L84-L84
Useful? React with 👍 / 👎.
Fixes speculative drafts with backend sampling on multi-slot servers: with
-np N, every slot except slot 0 gets wrong draft token ids, so DSpark / DFlash acceptance collapses for all concurrent requests but one.Problem
Draft contexts sample on the backend by default (
common_params_speculative::backend_sampling = true), with a top-k chain per sequence and one output per block position.llm_graph_context::build_samplingbuilds those chains for every sequence in every ubatch so the graph topology stays fixed, but only marks the chains of the sequence that is actually in the ubatch as outputs.ggml_gallocrdecides whether to re-plan a graph from node count and tensor sizes only, not from the output flags. A later graph with the same topology therefore reuses the address plan of the first one. Draft ubatches hold one sequence each, so the plan is made for the sequence-0 ubatch. There, the chains of sequences 1..N-1 are intermediates whose memory is freed and reused, which puts all candidate-id buffers of each such sequence at one address. When sequence k runs, every block position writes its top-k ids to that same buffer and the last one wins. The logits rows live elsewhere, so the probabilities look right while the ids are wrong.Symptoms on a server with N slots and N concurrent requests:
--no-spec-draft-backend-samplinghides the problemFix
In
build_sampling, flag the outputs of every chain (sampled, probs, logits, candidates) whether its sequence is active or not, so the allocation plan no longer depends on which sequence the first ubatch held. The plain logits view is skipped as before. This costs a few small extra output tensors per chain.A broader alternative would be to make
ggml_gallocrre-plan when a node's output flag differs from the stored plan. That touches every backend, so this change stays local to the sampling graph.Evidence
Same upstream base for both arms, CUDA build for sm_100 (B200),
Qwen3-4B-Q8_0.gguftarget,deepseek-ai/dspark_qwen3_4b_block7draft converted with--target-model-dir,--spec-type draft-dspark -np 8 -fa on, 8 different greedy chat requests sent at once, then the same 8 one at a time:The one-at-a-time numbers are the per-request ceiling. Before the fix most concurrent requests stay far below it; after the fix all of them reach it. Even one at a time the old build loses some acceptance, because the server can hand a sequential request to a slot other than 0.