Repository navigation
Conversation
nvfp4_mega_moe takes l2_activation_scale as a float kernel argument, so a CUDA graph replays the value it was captured with. When a weight update changes the down-projection input scale (for example QAT that recalibrates activation scales on every sync), a captured graph keeps quantizing the L2 input with the old scale while l2_alpha, a device vector, already holds the new one. l2_activation_scale may now also be a one-element CUDA float32 tensor. The epilogue reads it once, after cudaGridDependencySynchronize, so a replay sees values written after capture. A float keeps working as before. The op takes the tensor as a new trailing optional argument, so positional callers are unchanged. The test checks that a tensor scale matches the float, and that a captured graph picks up a scale and l2_alpha changed after capture (a stale scale would double the output), plus two malformed-tensor rejections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeFSSrpw7yTpLz2C2sHDKA Signed-off-by: Aaron Hao <aaronhao@inferact.ai>
Drop the float form: the kernel takes only a device pointer (null means a unit scale, like l1_alpha and l2_alpha), and the op takes `Tensor? l2_activation_scale=None` in the float's place. Passing a float now fails in Python, so no caller can bake the scale into a CUDA graph. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeFSSrpw7yTpLz2C2sHDKA Signed-off-by: Aaron Hao <aaronhao@inferact.ai>
This was referenced Oct 30, 2026
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.
nvfp4_mega_moetookl2_activation_scaleas a float kernel argument, so a CUDA graph replays the value it was captured with. vLLM computes this scale from the checkpoint's down-projectioninput_scale(the max over experts). When an RL weight update changes that scale (for example verl's NVFP4 W4A4 QAT, which sends the observed activation scales on every sync), a captured graph keeps quantizing the routed L2 input with the old scale whilel2_alpha, a device vector, already holds the new product. vLLM tracks this as a known limitation of reloading weights under CUDA graphs (vllm-project/vllm#64692).Change
l2_activation_scaleis now an optional one-element CUDA float32 tensor, likel1_alphaandl2_alpha. The epilogue reads it once, aftercudaGridDependencySynchronize, so a replay sees values written after capture.Nonemeans a unit scale.float l2_activation_scale=1.0becomesTensor? l2_activation_scale=Nonein the same position, and the Python API rejects a float. Callers that pass a float must passtorch.full((1,), scale, device='cuda')instead. vLLM's call site changes with its pin bump. The tensor must be float32, one element, ony's device. The old positive and finite check on the float is gone, since checking a device value would need a sync.AI/nvfp4-mega-moe.mddescribe the tensor form.Tests
On GB200 (SM100), 4 GPUs, torch 2.14 / CUDA 13.0:
tests/test_nvfp4_mega_moe.pypasses the scale as a tensor and gainscheck_graph_replay_reads_scales, run for every NVFP4 shape: capture a graph, double the scale andl2_alphain place, and require the replay to match an eager call with the doubled values. A replay that kept the captured scale would double the output.--check-apialso rejects a float scale, a float64 tensor and a two-element tensor.--tokens 1 17 129 --hidden 512 --intermediate-hidden 256 --experts 8 --check-api--input-scale 0.5 --weight-scale 0.25 --mid-scale 0.125--tokens 1 64 1024 --mid-scale 0.25 --weight-scale 0.5--x-scales --mid-scale 0.25None) scalesPerformance.
--benchmark, MiniMax-M3 shapes,--mid-scale 0.25, one node, max over ranks of the median kernel time, mean of 2 passes.devpasses the scale as a float, this PR as a tensor:vLLM follow-up
After this merges, vLLM bumps its DeepGEMM pin and passes a device buffer refilled in place from
DeepGemmMegaMoEExperts(and Step-5's MegaMoE). Its Triton prepare kernel, which computesl2_alpha, reads the same buffer.🤖 Generated with Claude Code
https://claude.ai/code/session_01SeFSSrpw7yTpLz2C2sHDKA