[Qwen3.8-Flash-Next] Separate prefill and decode paths for QSA indexer - #54513
Conversation
fb25a6d to
37227c3
Compare
2460bd1 to
6d87dc6
Compare
|
This pull request has merge conflicts that must be resolved before it can be |
6d87dc6 to
db191d6
Compare
|
/ci run |
|
✅ Triggered Buildkite CI #86528 for commit |
| (_TOPK_WORKSPACE_BYTES,), dtype=torch.uint8, device=q.device | ||
| ) | ||
|
|
||
| for query_start in range(0, rows, rows_per_chunk): |
There was a problem hiding this comment.
Do we need to optimize this for loop? or you want to leave it for future work?
There was a problem hiding this comment.
The loop follows existing DSv4 sparse indexer pattern to limit logits workspace size
The current main also has this chunking logic
| ) | ||
|
|
||
|
|
||
| def _topk( |
There was a problem hiding this comment.
vLLM has many place using top_k, could we unify them?
The condition looks strange
current_platform.has_device_capability(90)
and not current_platform.is_device_capability_family(120)
There was a problem hiding this comment.
I'm following the logic for DSv4 sparse indexer here as well
vllm/vllm/model_executor/layers/sparse_attn_indexer.py
Lines 617 to 629 in 55aa766
Add specialized Triton kernels for QSA prefill and decode, move batch splitting into metadata dispatch, cover speculative decode widths, and warm reachable decode specializations. Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Assisted-by: OpenAI Codex Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Assisted-by: OpenAI Codex Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Assisted-by: OpenAI Codex Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Assisted-by: OpenAI Codex Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Co-authored-by: Codex <noreply@openai.com> Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
db191d6 to
34ecc53
Compare
|
/ci run |
|
✅ Triggered Buildkite CI #86625 for commit |
An audit of this branch found numbers asserted in committed files that no artifact supports. Retracting them in place, since a wrong figure that stays in the repo is worse than no figure. Withdrawn: * MTP acceptance "3.00" (serve_mtp.sh) and "2.65-3.13" (FINDINGS.md). No acceptance value above 2.96 exists anywhere under repro-vast/; the estonia and lavd JSONs carry no acceptance data at all. The true range is 2.65-2.96 from four samples in fp8kv/fp8_metrics.txt:17-20. The sentence quoting 3.13 contradicted its own parenthetical in the same breath. * "Per-stream decode was faster at batch 6 than at batch 2". The two result files in the same directory say the opposite -- estonia_mtp.json (c=2) medians 66.59 tok/s/stream against estonia_final.json (c=6) at 43.82. This was the sole evidence for ranking the QSA indexer as the next bottleneck; byte accounting since prices it at 2-6% of the step. * "fp8 KV cache is unavailable", left behind in FINDINGS.md 57 lines below the section retracting it, and copied into two other files. The error string it quotes no longer exists in the tree. * The gather mechanism "roughly one pool task per row". Tasks are bounded by distinct shards (<=128); that is ~6x off for a decode gather and only true at batch 1. Commit 5efc12f's message carries the same error. Also corrected: a decode step is ~33 ms, not ~12.8 ms -- MTP emits ~2.7 tokens per engine step, so percentages computed as 1/tok-per-second were inflated ~2.7x. Fixed the :ro contradiction. The Dockerfile documented a read-only checkpoint mount while the entrypoint rewrote config.json in place, so the documented command failed for exactly the checkpoint the remap exists to serve. Mount is now documented read-write, with a pre-remapped checkpoint offered for :ro use. Added the measured dead ends -- b12x MoE, b12x MXFP8 linear, MADV_RANDOM, readahead, marlin atomic_add, flashinfer_cutedsl, and the upstream vllm-project#54513/ vllm-project#54517 rebase -- with the numbers, so they are not re-run. They converge on one finding: this deployment is memory-bandwidth bound on MoE expert weights, so no kernel substitution helps. Recorded that the engram working set is ~70 MB of rows, not the 26.8 GiB the table occupies; residency growth measures readahead, not demand. The earlier recommendation to buy 34 GiB of host RAM is withdrawn. Co-Authored-By: Claude Mythos <noreply@anthropic.com>
vllm-project#54513) Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
vllm-project#54513) Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Purpose
#53896 ships with a simple QSA indexer kernel that is used for both decode and prefill requests. This is not efficient as we can't design kernels specialized for decode and prefill shapes separately. This PR splits a mixed batch into decode and prefill requests, so that efficient decode/prefill kernels can be invoked separately, similar to how other attention backends work in vLLM.
Currently this PR ships 2 decode/prefill-specialized Triton kernels to replace the original generic QSA indexer, but these are not meant to be SOL. Further optimizations are left for future PRs.
Main changes
_qsa_mqa_paged_kernelwith_qsa_mqa_paged_uniform_kernel(decode and spec-decode) and_qsa_mqa_paged_prefill_kernel(prefill). Create a new fileqsa_indexer.pyunder NVIDIA ops.visible_blocks = (logical_position + 1) // compress_ratioto QSA metadata. Previously this is computed by the indexer score kernel, which is not quite natural._qsa_mqa_paged_uniform_kernel. The prefill kernel only has 1 specialization, it doesn't need warmup_LOGITS_WORKSPACE_BYTESto env varVLLM_SPARSE_INDEXER_MAX_LOGITS_MB, which raises the default workspace size from 128 MiB to 512 MiB. Decode kernel does not observe this limit, following DeepSeek sparse attention.The kernels' heuristics are currently tuned on GB300. They might not be optimal for all GPUs.
Microbenchmarks
All measurements are done on GB300. Using page_size=196 (actual page size for non-MTP production)
Decode and Spec-decode
Note: Decode query len (DQL) = 4 corresponds to MTP3 (3 speculative tokens)
Single request
Multiple requests Context lengths are sampled from log-normal distribution to simulate skewed contexts
Prefill
Prefill is still very far from SOL (2 PFLOPS). But good enough for a pure Triton baseline implementation. A more optimized kernel can be added in the future.
E2E perf benchmarks
Baseline e16b5e5. Results obtained on GB300 TP4. Qwen/Qwen3.8-Flash-Next
Non-MTP (P50 statistics)
MTP3 (
--speculative-config '{"method":"mtp","num_speculative_tokens":3}', P50 statistics)Test Plan
Unit test
E2E accuracy test
TP4 on GB300 with MTP3. Qwen/Qwen3.8-Flash-Next
Test Result
Essential Elements of an Effective PR Description Checklist
supported_models.mdandexamplesfor a new model.