Repository navigation
server, llama : --logits-topk, the top k of each GPU's share of the logits found on the GPU - #49
Merged
Merged
Conversation
…ogits found on the GPU The verify step of speculative decoding copies the logits of every verified position (248k floats each) to the host and samples over the whole vocabulary on the CPU. Under -sm tensor the 3060's share goes over PCIe 3.0 x4: a 49-token verify of --spec-select idled both GPUs ~7.7 ms (5.5 ms of it the copy), a 3-token MTP verify ~1.2 ms. - llama_set_logits_topk(ctx, k): after the decode's graph, a small graph per device (top_k + get_rows) on that device's share of the logits (the rows of the output head on it), queued on its stream; only the k candidates per share and row are copied. The sampled-logits API reports them with their tokens (also for the reordered head of --spec-draft-vocab); llama_get_logits_ith / llama_get_logits copy a whole row from the device on request, until a graph is computed again. Only when all outputs of the decode come from one ubatch and the logits are in device memory; otherwise all logits as before. - common_sampler_logits_topk(): the k a sampler chain needs to sample the same token: its top_k (1 when greedy) plus the tokens it can lower before (logit biases <= 0, the model's suppressed tokens, penalties of the last n); 0 for chains that read the whole distribution (n_probs, mirostat, DRY, top-n-sigma, top-p/min-p/typical/XTC before the top-k, dynamic temperature, positive biases). A grammar that rejects the sampled token and a token forced by the reasoning budget sample over a whole row (common_sampler_sample). - server: --logits-topk sets k per batch, the largest any slot in it needs, or 0 if one needs all logits. - ggml-meta: ggml_backend_meta_n_simple_backends / ggml_backend_meta_get_simple_backend. - test-llama-archs: the candidates hold the k largest logits of each row with their tokens and values, and a whole row read on request equals the row without top-k (bitwise), on every device config and with a draft-vocabulary list. Qwen3.8 27B, 4070 + 3060, -sm tensor, MTP 2 + --spec-select + draft list, 4 requests per task (median), two runs: docker-compose edit 115 -> 131-133 t/s at temperature 1.0 and 120 -> 132-137 greedy, mean of compose edit, code edit, German letter and explanation +7-9%; greedy and seeded outputs bitwise identical (6 of 6). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…decodes, raising penalties - A whole row is gone once another graph is computed, also by the MTP draft context in the target's buffers before the sampling (--phase-aware-workspace): a grammar resample or a forced token aborted the server. The server now takes all logits for a slot whose grammar constrains the next token (common_sampler_needs_all_logits: a lazy grammar waiting for its trigger constrains none, llama_sampler_grammar_waiting) and for a replay of a slot with a grammar; a draft verification stops at the token that starts the grammar (the next step samples from all logits). The token the reasoning budget forces joins the candidates (common_reasoning_budget_get_forced): it needs no row. - Top-k only when the outputs are in the last ubatch of the decode: the later ubatches overwrote the rows. - Penalties that raise logits (negative frequency / presence penalty, repeat penalty < 1) take all logits. - llama_get_logits_topk_active(); test-llama-archs checks outputs in the first and in the last of three ubatches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner
Author
Review: 3 findings, fixed in 1005c471. HIGH: whole rows gone before the sampling (server abort).
2. HIGH: top-k with the outputs in the first of several ubatches. The later ubatches overwrote the rows. Top-k now runs only when the outputs are in the last ubatch of the decode. test-llama-archs checks outputs in the first and in the last of three ubatches. 3. MEDIUM: penalties that raise logits. A negative frequency or presence penalty, or a repeat penalty < 1, now takes all logits. Verified (production model and args,
|
| case | before | after |
|---|---|---|
| reasoning budget 8 tokens | abort | ok, output equals the run without top-k |
grammar 7[0-9]{3} |
abort | ok, 7189 as without top-k |
| grammar-heavy slot generating while another slot prefills 44k tokens | abort | both ok |
frequency_penalty -1.0, seed 42 |
different text | same text as without top-k |
| reviewer's multi-ubatch repro (outputs in the first ubatch) | abort | whole rows equal the reference |
- Tool-call chats (two tools, three prompts, greedy and seeded), MTP drafts: the output is the same with and without
--logits-topk, compared without the random call ids. Two runs without top-k were also equal to each other. - Production drafts (
ngram-simple,draft-mtp --spec-select, draft vocabulary, compact mask): grammar, budget, tool calls and the 44k-prefill case all without abort. - test-llama-archs
-a '^(llama|qwen35|qwen35moe)$': 64/64 passed.
🤖 Generated with Claude Code
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.
Closes #42 (GPU sampling of the verify positions under -sm tensor; after the GPU sampler of Strata by @Niko1221, MIT).
Problem
After every verify step the target's logits are copied to the host whole: 248k floats per verified position. The CPU sampler chain then runs over the whole vocabulary. Under
-sm tensorthe 3060's share of each row goes over PCIe 3.0 x4.nsys of a docker-compose edit with MTP +
--spec-select(#47):That is ~126 ms of ~3 s of generation.
Change
llama_set_logits_topk(ctx, k). After the decode's graph, each device runs a small graph (top_k+get_rows) on its share of the logits, i.e. the rows of the output head it holds. The graph is queued on that device's stream, and only the k candidates per share and row are copied.llama_get_sampled_logits_ith/_candidates_ith) reports them with their tokens. This also works for the reordered head of--spec-draft-vocab(spec : the MTP draft head as a view of the reordered output head, without memory of its own #46).llama_get_logits_ith/llama_get_logitscopy a whole row from the device on request, until a graph is computed again.TOP_Kfor 3 rows takes 44 us (4070, 155k columns) / 50 us (3060, 93k); for 49 rows 148 / 412 us.common_sampler_logits_topk(params, vocab). It returns the k that a sampler chain needs to sample the same token: itstop_k(1 when greedy) plus the tokens it can lower before that point. Those are logit biases <= 0, the model's suppressed tokens, and penalties of the last n tokens. Production: 20 + 64 = 84.n_probs, mirostat, DRY, top-n-sigma, top-p/min-p/typical/XTC before the top-k, dynamic temperature, positive biases.common_sampler_sample). So tool-call grammars still work, and they cost a row copy only when they intervene.--logits-topk(opt-in). k is set per batch to the largest that any active slot needs, or 0 if one slot needs all logits.ggml_backend_meta_n_simple_backends/ggml_backend_meta_get_simple_backend.Results
Qwen3.8 27B UD-Q4_K_S, 4070 + 3060,
-sm tensor, MTP 2 +--spec-select+ draft list; 4 requests per task (median), temperature 1.0 / greedy:--logits-topk(run 1)(The "without" column is run 1; the base of run 2 was 71.5 / 76.8 with some CPU load.)
test-llama-archs -a '^(llama|qwen35|qwen35moe)$': 64 of 64 passed, with a new check on every device config, also with a draft-vocabulary list (reordered head).TOP_Ktemporaries come from the CUDA pool, ~4 MB for 98 rows.🤖 Generated with Claude Code