[Bugfix] Share FlashInfer B12x MoE workspace across layers - #48698
[Bugfix] Share FlashInfer B12x MoE workspace across layers#48698dumko2001 wants to merge 1 commit into
Conversation
|
👋 Hi! Thank you for contributing to the vLLM project. 💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in PRs do not trigger a full CI run by default. Once the PR is approved and ready to go, your PR reviewer(s) can run CI to test the changes comprehensively before merging. To run CI, PR reviewers can either: Add If you have any questions, please reach out to us on Slack at https://slack.vllm.ai. Agent GuidelinesIMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban. 🚀 |
8f8494c to
05c8d53
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8f8494c3ff
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
3088550 to
9a7c8ff
Compare
9a7c8ff to
c154fc8
Compare
|
Current-head audit ( Confirmed in the installed FlashInfer 0.6.14 source that the functional path owns a process-global workspace cache keyed by expert/hidden/top-k/device/backend/quantization/activation geometry. It reuses an entry with sufficient routed-row capacity and replaces it only when a larger capacity is required. That validates the ownership model in this PR and avoids one wrapper workspace per MoE layer. The strengthened vLLM contract test now checks the full functional call surface—input/output identity, output dtype, routing tensors, both weights and scale-factor layouts, both alphas, I did not claim an actual B12x kernel/DP2/model pass. The available host had driver 570 and CUDA 12.8; the installed FlashInfer implementation explicitly rejects SM12x below CUDA 13 ( |
3389083 to
2195454
Compare
|
Architecture correction on current head The PR now keeps FlashInfer's caller-owned The tests prove one wrapper for two layers, DP-scaled capacity, distinct |
2195454 to
68d8131
Compare
Own one fixed-capacity B12x wrapper per model geometry and share it across sequential expert layers. Scope sharing to one live vLLM configuration, scale capacity for DP before graph capture, keep workspace pointers stable, and release the wrapper when its model owners die. Addresses vllm-project#47982 Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Assisted-by: OpenAI Codex Signed-off-by: Sidharth Rajmohan <dumko.raj@gmail.com>
68d8131 to
78388ad
Compare
|
I independently validated exact head Public evidence:
ScopeThis is real-hardware validation of shared-wrapper ownership and lifecycle at The tested model requires approximately 30 GiB per complete replica. This host The PR's CPU contract tests cover DP-scaled capacity construction for Primary resultPrimary checkpoint: Unsloth The exact base reproduced the per-layer B12x workspace OOM during its first With only the PR diff changed, the exact head completed profiling and served
A focused two-layer A/B CUDA-graph diagnostic used distinct layer weights and Serving, stability, and performance
Eager-allocation lifecycleBecause the PR moves wrapper construction into model initialization and the Both cycles restored successfully. Deterministic temperature-zero output Additional checkpoint and remaining scopeThe NVIDIA ModelOpt W4A16 checkpoint also started, avoided the per-layer Final scope assessment:
|
…orkspace across layers Cherry-picks unmerged upstream PR vllm-project#48698 (dumko2001) to fix the per-layer B12xMoEWrapper workspace blowup that hard-wedged GB10 DGX Spark nodes. Root cause (diagnosed via ora-1 + lib-1, 4 physical wedges): FlashInferB12xExperts allocated a B12xMoEWrapper static workspace lazily on first apply(), PER MoE LAYER. On GB10 UMA (shared 121GiB pool) this summed to a multi-GiB blowup during profile_run() — before any KV/util knob applies — exhausting the pool and starving the kubelet (vLLM vllm-project#47982, vllm-project#49476). --gpu-memory-utilization, --enforce-eager, max-num-batched-tokens all failed to bound it. The patch: - Weakref-shared wrapper registry keyed by (owner, geometry): ONE wrapper per geometry+vllm-config instead of one per layer. - Eager acquisition in __init__ so the workspace lands in the profiling baseline (not lazily in apply()). - DP fix: max_num_tokens = moe_config.max_num_tokens * dp_size. Preflight verified: - git apply --check clean on gb10-main (no local mods to this file). - flashinfer 0.6.15.post1 B12xMoEWrapper.__init__ accepts the new output_dtype + device kwargs (kwonly). Still necessary-but-not-sufficient for GB10: the UMA shared-pool overshoot (vllm-project#46307) is independent — keep gpu-memory-utilization <=0.6 + --enforce-eager on first boot. Upstream vllm-project#48698 is DRAFT/unmerged; drop this carry once it lands.
…orkspace across layers Cherry-picks unmerged upstream PR vllm-project#48698 (dumko2001) to fix the per-layer B12xMoEWrapper workspace blowup that hard-wedged GB10 DGX Spark nodes. Root cause (diagnosed via ora-1 + lib-1, 4 physical wedges): FlashInferB12xExperts allocated a B12xMoEWrapper static workspace lazily on first apply(), PER MoE LAYER. On GB10 UMA (shared 121GiB pool) this summed to a multi-GiB blowup during profile_run() — before any KV/util knob applies — exhausting the pool and starving the kubelet (vLLM vllm-project#47982, vllm-project#49476). --gpu-memory-utilization, --enforce-eager, max-num-batched-tokens all failed to bound it. The patch: - Weakref-shared wrapper registry keyed by (owner, geometry): ONE wrapper per geometry+vllm-config instead of one per layer. - Eager acquisition in __init__ so the workspace lands in the profiling baseline (not lazily in apply()). - DP fix: max_num_tokens = moe_config.max_num_tokens * dp_size. Preflight verified: - git apply --check clean on gb10-main (no local mods to this file). - flashinfer 0.6.15.post1 B12xMoEWrapper.__init__ accepts the new output_dtype + device kwargs (kwonly). Still necessary-but-not-sufficient for GB10: the UMA shared-pool overshoot (vllm-project#46307) is independent — keep gpu-memory-utilization <=0.6 + --enforce-eager on first boot. Upstream vllm-project#48698 is DRAFT/unmerged; drop this carry once it lands.
…orkspace across layers Cherry-picks unmerged upstream PR vllm-project#48698 (dumko2001) to fix the per-layer B12xMoEWrapper workspace blowup that hard-wedged GB10 DGX Spark nodes. Root cause (diagnosed via ora-1 + lib-1, 4 physical wedges): FlashInferB12xExperts allocated a B12xMoEWrapper static workspace lazily on first apply(), PER MoE LAYER. On GB10 UMA (shared 121GiB pool) this summed to a multi-GiB blowup during profile_run() — before any KV/util knob applies — exhausting the pool and starving the kubelet (vLLM vllm-project#47982, vllm-project#49476). --gpu-memory-utilization, --enforce-eager, max-num-batched-tokens all failed to bound it. The patch: - Weakref-shared wrapper registry keyed by (owner, geometry): ONE wrapper per geometry+vllm-config instead of one per layer. - Eager acquisition in __init__ so the workspace lands in the profiling baseline (not lazily in apply()). - DP fix: max_num_tokens = moe_config.max_num_tokens * dp_size. Preflight verified: - git apply --check clean on gb10-main (no local mods to this file). - flashinfer 0.6.15.post1 B12xMoEWrapper.__init__ accepts the new output_dtype + device kwargs (kwonly). Still necessary-but-not-sufficient for GB10: the UMA shared-pool overshoot (vllm-project#46307) is independent — keep gpu-memory-utilization <=0.6 + --enforce-eager on first boot. Upstream vllm-project#48698 is DRAFT/unmerged; drop this carry once it lands.
Purpose
#43328 replaced FlashInfer SM12x B12x MoE's functional API with one
B12xMoEWrapperper expert layer. Every wrapper owns large graph-stable scratchbuffers. That creates both failures reported in #47982: DP all-gather can exceed
the per-rank capacity, while persistent workspace is multiplied by the number
of MoE layers. Scaling every per-layer wrapper by
dp_sizeavoids the exceptionbut worsens the memory amplification.
This PR keeps FlashInfer's caller-owned wrapper contract but weakly shares one
fixed-capacity wrapper across all live expert layers with the same geometry.
Capacity is
max_num_tokens * dp_size, established before graph capture. Thelayers keep the wrapper alive; a weak registry does not extend model lifetime,
so the wrapper and its GPU scratch are released after the last owning model
layer is destroyed.
This deliberately does not use FlashInfer 0.6.14's functional API: that path
stores mutable scratch in a process-global cache with no public clear/lock
contract and can grow outside vLLM's graph-lifetime control.
Addresses #47982. Use
Fixes #47982only after the DP2 crash and DP1/DP2 memoryregressions pass on CUDA-13 SM120. Mandatory duplicate searches found no
competing PR that repairs both capacity and per-layer amplification.
AI assistance was used. The human submitter must review every changed line and
the recorded hardware/model results before readiness.
Implementation
DP-scaled capacity, local experts, dtype, device, and activation.
which occurs inside vLLM's normal model-load memory-pool context.
scales, alphas, routing tensors, and FC2 input scale on every call.
vLLM DBO does not create a scratch race here: every ubatch context receives the
same compute stream, CPU execution is explicitly serialized, and the B12x call
has no yield inside it. However, today's naive-DP prepare path is synchronous
and the modular kernel rejects DBO before reaching the wrapper. This PR does
not add DBO support.
Validation
Current head
78388ad4ais based on upstream1a659a0c3.The contract tests prove that two layers under one model owner construct one
wrapper, different live model owners do not alias mutable scratch, DP2 capacity
is doubled without the unrelated 8192-token autotuning floor, each layer's
distinct tensors reach
run, output is copied into the modular-kernel buffer,and dropping the final owners releases the wrapper.
No B12x kernel, DP2, memory, graph, sleep/reload, or exact-model result is
claimed on this head. The available two-RTX-5090 host exposed SM120 but CUDA
12.8; pinned FlashInfer requires CUDA 13.
Adjacent exact-model work
The #47982 reporter checkpoint is ModelOpt
MIXED_PRECISIONwith W4A16 NVFP4experts. Current main's B12x selection accepts that scheme, but semantic
quant_mode="w4a16"andsource_format="modelopt"are not yet propagated tothe wrapper. #43929 tracks that separate correctness gap and must be
rebased/reduced or explicitly handed off rather than duplicated here.
#48536/FlashInfer #3932 concern the W4A4 scale path. #49031 concerns prepared
W4A16 weight duplication and source-weight lifetime. Workspace sharing does not
solve either issue, so exact-model correctness and peak residency must be
measured on the appropriate stacked revisions.
Required before readiness
On a CUDA-13-capable two-SM120 host:
current main.
rejection remains clear rather than claiming DBO support.
numerical reference.
layerwise weight reload, whose prepared-weight lifetime is broader than this
workspace fix.
deterministic output/evaluation against Marlin.
Release note
Share graph-stable FlashInfer B12x MoE scratch across layers and size it for
data-parallel gathered tokens, addressing the DP capacity crash without
multiplying multi-GiB workspace by every MoE layer.
After complete SM120 validation and mainline merge, request a v0.26 backport.