Skip to content

fix: bound the DSA kpool ragged MQA logits allocation - #38469

Closed
SouthWest7 wants to merge 1 commit into
sgl-project:mainfrom
SouthWest7:fix/dsa-kpool-ragged-mqa-logits-oom
Closed

SouthWest7 wants to merge 1 commit into
sgl-project:mainfrom
SouthWest7:fix/dsa-kpool-ragged-mqa-logits-oom

Conversation

@SouthWest7

@SouthWest7 SouthWest7 commented Sep 8, 2026 •

Copy link
Copy Markdown

The kpool indexer's ragged extend path builds one dense fp32 logits tensor whose two dimensions are both summed over the whole batch, while each q row may only read its own request's column window. Peak memory is therefore quadratic in the request count and each row's useful area is ~1/B of the allocation, so long-context traffic with prefix-cache hits kills every TP rank inside deep_gemm.fp8_mqa_logits.

The non-kpool indexer already had a memory budget for the same tensor, but the kpool path carried a dead, drifted copy of the predicate and never called it. Move the policy to one module both flavors import, and split the kpool computation per request and then per q-row chunk under that budget. The single-call fast path is preserved unchanged when the dense tensor fits.

Fixes #37712

Motivation

Modifications

  • New dsa/mqa_logits_budget.py — single home for the budget policy, since both indexer flavors need it and they are not in a subclass relationship. A second copy is how the kpool path drifted in
    the first place.
  • dsa_indexer.py — drop the class constants and the three budget methods, import the module functions instead.
  • dsa_indexer_kpool.py — delete the dead duplicate; consult the shared budget, and when the dense tensor does not fit, split per request (plan.ragged_groups) and then per q-row chunk. Every
    column index rebases into the sliced K window. The single-call fast path is preserved unchanged when it fits.
  • kpool_plan.py — add RaggedGroup and KPoolExtendPlan.ragged_groups, the host-side geometry of the ragged concat layout, built from CPU lists already computed in the plan so there is no
    device→host sync.

Accuracy Tests

GLM-5.3-Flash on 8× B300-class GPUs, serving shape from the issue.

Chunking must not change which KV tokens the indexer selects. 4 passkey-retrieval prompts (~72 K tokens, needles at four depths, greedy), sent serially so batch composition is fixed, with
SGLANG_DSA_MQA_LOGITS_FREE_MEM_FRACTION large (dense path) vs 0.001 (~32 q-row chunks per step):

result
dense 4/4
chunked 4/4, byte-identical output text

Speed Tests and Profiling

12 × 132096-token contexts in one batch (18.14 GiB dense, i.e. above the budget so the fix does chunk), warm repeats with /flush_cache between each:

wall
chunked 0.6 / 0.6 / 0.7 s
dense (budget raised) 0.6 / 0.6 / 0.6 s

No measurable cost. Note the first long-context batch after server start is 3–20× slower from deep_gemm JIT; only warm repeats are comparable.

Checklist

Review and Merge Process

  1. Ping Merge Oncalls to start the process. See the PR Merge Process.
  2. Get approvals from CODEOWNERS and other reviewers.
  3. Trigger CI tests with comments or contact authorized users to do so.
    • Common commands include /tag-and-rerun-ci, /tag-run-ci-label, /rerun-failed-ci
  4. After green CI and required approvals, ask Merge Oncalls or people with Write permission to merge the PR.

CI States

Latest PR Test (Base): ❌ Run #35491432054
Latest PR Test (Extra): ❌ Run #35491431816
Latest PR Test (AMD ROCm 10): ❌ Run #35491431937

@JackZeng0208

Copy link
Copy Markdown
Contributor

Thanks for fixing the issue I encountered in #37712. I also found several related PRs, including #35759, #38086, and #38101. Since this issue appears across different accelerators and has been discussed in multiple PRs, it may be worth considering a follow-up redesign that combines budgeted workspaces with streaming top-k. Per-request Q chunking still requires a full K-length logits row, while other temporary allocations remain outside the workspace budget, so a more comprehensive memory-bounded design could help address the underlying problem. I would like to work on such re-design based on this PR if needed.

cc @SouthWest7 @1am9trash @Fridge003

@Fridge003

Copy link
Copy Markdown
Collaborator

Hi @SouthWest7 , can you please rebase on main, and reuse the util functions introduced in #39095, thanks~

@Fridge003

Copy link
Copy Markdown
Collaborator

@JackZeng0208 , we just merged #39095, which will be a starting point for fixing this OOM issue in an unified way.
We will merge following fixing PRs on dsv4.1/glm53 flash

@SouthWest7
SouthWest7 force-pushed the fix/dsa-kpool-ragged-mqa-logits-oom branch 2 times, most recently from f1e32cf to f804ff4 Compare September 20, 2026 04:41
@SouthWest7

Copy link
Copy Markdown
Author

@Fridge003 Hi, Could you help review this?

The kpool indexer's ragged extend path builds one dense fp32 logits tensor
whose two dimensions are both summed over the whole batch, while each q row
may only read its own request's column window. Peak memory is therefore
quadratic in the request count and each row's useful area is ~1/B of the
allocation, so long-context traffic with prefix-cache hits kills every TP
rank inside deep_gemm.fp8_mqa_logits.

The kpool path carried a dead, drifted copy of the budget predicate and never
called it. Delete it, take the shared policy from attention/mqa_logits_utils.py
(sgl-project#39095) instead, and split the kpool computation per request and then per
q-row chunk under that budget. The single-call fast path is preserved
unchanged when the dense tensor fits.

Fixes sgl-project#37712
@SouthWest7
SouthWest7 force-pushed the fix/dsa-kpool-ragged-mqa-logits-oom branch from f804ff4 to e3e631d Compare September 20, 2026 05:20
@SouthWest7 SouthWest7 closed this Oct 13, 2026
@SouthWest7
SouthWest7 deleted the fix/dsa-kpool-ragged-mqa-logits-oom branch October 13, 2026 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] GLM-5.3-Flash: CUDA OOM in fp8_mqa_logits during long-context prefill kills all TP ranks

3 participants