[SM120] flash_mla: allocate the page-split buffer outside inference mode - #35116
Conversation
The lazy persistent buffer in `_split_kv_pages_to_64` is first allocated during autotune, which runs under inference mode, and is written again during CUDA graph capture, which does not. Mutating an inference tensor there raises. Allocate it under `torch.inference_mode(False)` so the buffer outlives the mode it was created in. Reported downstream while running DSV4 on SM120. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fridge003 asked for smaller PRs instead of one large one carrying several independent optimizations. Two of them are not SM120 work at all and now live on their own: sgl-project#35116 allocate the page-split buffer outside inference mode (correctness fix) sgl-project#35118 fuse the hc-prenorm combine step into a Triton kernel (2.75-10.6x) Both are reverted here, so this PR keeps only the SM120 enablement. The triton imports in mhc.py went with hc_combine; the remaining SM120 change in that file is the TileLang warp-specialization workaround. This PR now depends on sgl-project#35116: without it, the page-split buffer is allocated under inference mode during autotune and CUDA graph capture cannot write to it. Will rebase once that lands. A third candidate, vectorizing the page-split copy in u64 lanes, measured within noise on RTX 6000D (119.3 -> 121.1, 429.8 -> 433.4, 541.6 -> 540.1 GB/s at 2048/ 8192/16384 pages), so it is not worth a PR of its own and stays here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The original configuration still exists, and I re-ran it today on both the On the v0.5.16 stack (autotune enabled, DSPARK, CUDA graphs; exact flags in the fold
On current main with the same decisive flags (TP2, DSPARK, autotune enabled), first Why row two serves despite holding a live inference tensor: the split buffer is Exact configurations and July historyv0.5.16 stack: sglang v0.5.16 with the #32320 backport, torch 2.11, DeepSeek-V4-Flash Current main (2e7c85d): DeepSeek-V4-Flash-0731, TP2, July history, stated precisely: what I observed in July was this failure class on the Row three of the table doubles as a live boot-time reproduction of the failure class, |
Purpose
_split_kv_pages_to_64keeps two lazily allocated persistent buffers in the samebuffersdict, with the same lifetime: allocated once on first use, reused acrossautotune, CUDA graph capture and steady-state serving.
On current main only one of them is protected:
The reasoning in that comment applies verbatim to
buf: it is written during CUDA graphcapture too. This PR closes the asymmetry.
The
mbufguard was added after @moxcat reported the failure while running DSV4 onSM120; the fix at the time covered both lazy buffers in this function, and the
bufhalf was lost somewhere between that backport and main. The same pattern, with the same
justification, also exists in
sglang/srt/layers/moe/moe_runner/flashinfer_cutedsl.py.When it does fire, the symptom is:
What I could and could not verify
I could not construct a configuration on 4x RTX 6000D (SM120) where the unguarded
allocation actually lands under inference mode. Instrumenting the allocation site:
--moe-runner-backend deep_gemminference_mode=False--moe-runner-backend flashinfer_mxfp4, DeepSeek-V4-Flash-0731inference_mode=FalseIn both,
bufis first allocated duringCapturing batches, so the ordering neverarises. So I am not attaching a before/after reproducer — the case for this change is
the asymmetry with
mbuf, not a failure I can demonstrate on demand.@moxcat — if you still have the configuration that hit this originally, it would turn the
table above into a real before/after.
Context
Split out of #29927 at @Fridge003's request, since it is not SM120 enablement work.
#29927 now depends on it.
Scope
One allocation site. Buffer contents, shape, caching key and lifetime are unchanged;
only the mode it is allocated under. No-op wherever the allocation already happens
outside inference mode.