Repository navigation
perf(inference): top-p-only sampling replay with CSR support capture (no top_k cap, no bitmask) - #4286
Merged
Conversation
…/topp-ragged-capture # Conflicts: # tests/unit/inference/test_serving_tokens.py
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.
Stacked on #4232 (base
feat/sc-exact-support). Allows sampling replay (and exact score centering) with top-p alone: no injectedtop_k, no width cap, no full-vocab bitmask.Why
[rows, min(batch max top_k, 2048)]ids plus a full[rows, V/8]bitmask on every step. The bitmask is always copied to the host.widthcolumns, so a top-p-only row wider than the cap fails as incomplete evidence. That is why the config injectstop_k = 128and rejectstop_k > 512.What
Capture (
paired_capture.py,request_replay.py)from_logitsarguments and builds the supports after sampling. It uses two Triton passes over the processed logits, which vLLM's sampler has already masked to the nucleus:(logprob bits << 32) | id, into one flat device buffer.inference.sampling_support_capacity, default 16M entries (128 MB).vllm.prime_rl.sampling_support).Validation
temperature > 0. vLLM'stop_k > 0requirement for mask capture is dropped for them.Config
TRAIN_TOP_K_DEFAULTinjection,TRAIN_TOP_K_BOUND, and the "cannot mix top_k > 0 and -1" check. That check existed only because the capture width came from the batch max top_k.logprobs >= 1to opt into compact evidence.vLLM's top-p is not exactly sort-based top-p
The sampled token is always drawn from exactly the captured set, because capture reads the logits the sampler masked. So replay stays consistent.
vLLM 0.32's Triton top-p (
apply_top_k_top_p_triton) does not always keep the sort-based nucleus. Compared with an fp64 stable-sort reference, 64-128 rows × 151936:On peaked, LLM-like rows it is nearly always exact. On flat rows the kept mass can fall below p, and rows with many tied logits differ widely. This PR leaves vLLM's top-p as is.
Numbers (H200, chat prompts, T=1, 1024 forced tokens per request, median of 3)
tok/s.
replay= mask capture only;sc= replay plus compact sampler logprobs.Qwen3-1.7B, 1 GPU, 512 concurrent
Kept per token with top-p only, Qwen3-1.7B: mean 174-213, p99.9 42k-69k, max 151934 (the whole vocab). This is the forced-length regime: once the answer is done, the model's distribution goes flat. With top_k 2048: mean 7-11.
GLM-4.5-Air, TP 4, 256 concurrent
GLM top-p only: kept mean 13-24, p99.9 250-520, max 151549 (the whole vocab).
scdrop on Qwen (16-18k vs 21k tok/s) comes from the API server serializing the compact logprobs of full-vocab rows (mean 174 entries per token). It tracks the forced-length tail. The GLM tail is milder, and there the drop is 1-6%.Capture kernel alone, 512 rows × 151936 with a wide synthetic distribution (9.3M kept): 0.87 ms. vLLM's fixed-width capture at K=2048 takes 0.22 ms (it can't hold those rows); top-p masking takes 3.1 ms.
End-to-end
Qwen3-0.6B reverse-text, 10 steps, icepop plus exact score centering, top_p 0.95 with no top_k. It was run together with the ragged trainer (stacked draft on #4233).
Every token was replayed and centered exactly over its full recorded support (head mass 1, tail 0).
Tests
pytest tests/unit/orchestrator tests/unit/test_configs.py tests/unit/inference -m "not gpu"on a CPU node: 413 passed. Ruff is clean.tests/unit/inference/test_serving_tokens.py, which includes a new GPU test, passes. The new test checks that the capture equals the masked set, that logprobs equal log_softmax over the kept set, that sampler rows are merged, and that overflow yields -1.Note: #4233 rewrote the score-centering validator. Merging #4232 into #4233 after this PR will conflict in
rl.py. The resolution is the same one-line change (k = (sampling.top_k or 1)for truncated sampling).