Skip to content

Quantized weight-update fixes: Qwen3.5 draft KV budget, UE8M0 partial blocks, mxfp8 fused indexer, NVFP4 input scales - #43251

Merged
Zhichenzzz merged 13 commits into
yueming/p2p-s1from
yueming/p2p-quant-fixes
Oct 12, 2026
Merged

Zhichenzzz merged 13 commits into
yueming/p2p-s1from
yueming/p2p-quant-fixes

Conversation

@yueming-yuan

@yueming-yuan yueming-yuan commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #42820. The model- and format-specific engine fixes miles' p2p weight update needs; #42820 keeps the general engine support.

Derived state after writes that bypass a model's loader (moved from #42820, plus one gap the review found):

fix symptom cause
GemmaRMSNorm gemma_weight Qwen3.5 serves the old norm after a p2p update; fused allreduce kernels read gemma_weight the norm's weight loader derives gemma_weight = weight + 1, and p2p, remote-instance, sharded-state and dummy loads write the weight without it; post_load_weights now refreshes it
gemma_weight at every session end a session whose direct or custom load wrote the norm weight kept the old gemma_weight those loads bypass the norm's loader yet mark the session loaded, so end_weight_update skipped post_load_weights; it now refreshes gemma_weight either way
GLM-lite NextN post-load the GLM-4.7-Flash draft skipped its NextN post-load after a p2p update post_load_weights ran the target's path on the draft; the NextN draft now pins is_nextn=True

Quantized checkpoints. Four fixes found by running miles' p2p weight-update e2e with quantized rollout checkpoints on GB300 (fp8 block, mxfp8, nvfp4 on GLM-5.2 cut to 5 layers; fp8 on Qwen3.5-35B-A3B cut to 4 layers with its MTP draft). None is specific to p2p: each also hits sglang's own weight update or its startup. One commit each.

fix symptom cause
EAGLE draft KV budget for Qwen3.5 the draft's KV pool is allocated outside mem_fraction_static; OOM at startup on the 4-layer cut (127.6 GB draft pool, 82 GB left at 0.7) _resolve_eagle_aux_hidden_state reads the draft depth from num_nextn_predict_layers; Qwen3.5 names it mtp_num_hidden_layers, so the target's per-token cost counted no draft layer
UE8M0 scales of non-128-multiple rows RuntimeError: shape '[20, 128, 48]' is invalid for input of size 125952 in the weight checker on every GLM-5 fp8 update on Blackwell _inverse_transform_scale_ue8m0_impl viewed the per-row packed scales as whole 128-row blocks; GLM-5's fused q_a + kv_a has 2624 rows
mxfp8 shards of the fused indexer the fused bf16 wk_weights_proj off by up to 5e4 after loading an mxfp8 checkpoint with a quantized wk _load_fused_indexer_wk dequantized with block_quant_dequant, multiplying by the uint8 UE8M0 exponents as integers
NVFP4 MoE input scales in the weight check every NVFP4 MoE update fails the check on w13_input_scale / w2_input_scale only they are calibrated activation scales no update carries (miles' quantizer emits weight, weight_scale, weight_scale_2); the checker reset them to random before the update. Now _skip_weight_check, as the KV cache scales

On the draft budget: with Qwen3.5-35B-A3B's 10 full-attention layers the unbudgeted draft is a tenth of the KV pool, which fits at 0.6–0.7 and so went unnoticed; the 4-layer cut makes it as large as the target's.

Tests

  • unit/model_loader/test_default_model_loader.py: gemma_weight follows a norm weight written without its loader (CPU).
  • unit/model_executor/model_runner_components/test_weight_updater_published_storage.py: a session whose load bypassed the norm's loader still refreshes gemma_weight (CPU); fails on its parent.
  • unit/models/test_glm4_moe_lite_nextn_post_load.py: the GLM-lite NextN draft runs the NextN post-load (CPU).
  • unit/model_executor/model_runner_components/test_spec_aux_hidden_state.py: an MTP depth named mtp_num_hidden_layers counts as the EAGLE draft's layers (CPU).
  • unit/layers/quantization/test_fp8_utils.py: the UE8M0 round trip also covers GLM-5's (2624, 6144) (CUDA).
  • unit/models/test_deepseek_fused_indexer_mxfp8.py: mxfp8 wk and weights_proj reach the fused param dequantized with their UE8M0 scales (CPU).
  • unit/layers/quantization/test_modelopt_nvfp4_moe_weight_check.py: the checker skips the NVFP4 MoE input scales and still checks the weight scales (CPU).

On an H200 devbox (radixark/miles:dev), all changed test files of this branch and #42820 pass (35 passed, 1 skipped: an SM100-only case). On #42820's head each quantized case fails for its cause: None == 1, the [20, 128, 48] view error, the fused wk mismatch, w13_input_scale not skipped.

End to end on GB300 with the UE8M0 fix applied: GLM-5.2 5-layer fp8 compares every fp8 tensor within quantization ULP, fused_qkv_a_proj_with_mqa included. What remains there is on miles' side: its fp8 / mxfp8 converters quantize the interleaved indexer's wk, which sglang keeps in bf16 and the trainer sends in bf16.


CI States

Latest PR Test (Base): ❌ Run #38300076576
Latest PR Test (Extra): ❌ Run #38300075941
Latest PR Test (AMD ROCm 10): ❌ Run #38300076381

…dden_layers

The target's KV pool sizing adds the EAGLE draft's layers to the per-token
cost, taking the draft's depth from num_nextn_predict_layers. Qwen3.5 names
it mtp_num_hidden_layers, so with the target's own MTP layer as draft the
depth read as none, and the draft's pool, sized to the target's token
count, was allocated outside mem_fraction_static: about a tenth more KV
than budgeted on Qwen3.5-35B-A3B, and an out-of-memory at startup on a
4-layer cut of it, whose one full-attention layer makes the draft's pool
as large as the target's (127.6 GB with 82 GB left at 0.7 on GB300).
The packed UE8M0 layout holds one scale row per weight row, so a weight of
2624 rows (GLM-5's fused q_a + kv_a) ends in a partial block. The inverse
viewed the rows as whole blocks of 128 and raised `shape '[20, 128, 48]' is
invalid for input of size 125952`, which made the weight checker fail on
every GLM-5 fp8 update on Blackwell. It now takes the first row of each
block and checks every row of a block against it, the partial one included.
…heir UE8M0 scales

On CUDA the DSA indexer loads wk and weights_proj into one bf16 param,
dequantizing quantized shards with block_quant_dequant, which multiplies
by the scale. mxfp8 checkpoints store 1x32 UE8M0 scales as uint8
exponents, so an mxfp8 GLM-5.x checkpoint with a quantized wk (as miles'
converter makes) started with indexer keys scaled by the raw integers:
off by up to 5e4 against the bf16 the trainer's first update writes.
Shards with uint8 scales now go through dequant_mxfp8_2d_to_bf16.
w13_input_scale and w2_input_scale are calibrated activation scales, like
the KV cache scales: no weight update carries them (miles' NVFP4 quantizer
emits weight, weight_scale and weight_scale_2 only). The checker reset
them to random before an update and compared them after it, so every
NVFP4 MoE update failed the check on them alone, whatever it wrote. They
are now marked _skip_weight_check, as the KV cache scales are.
Both rebuild model-specific derived state after a write that bypasses
the model's own loader. #43251 carries them with the
other model- and format-specific fixes p2p needs, so this PR keeps the
general engine support.
loader.post_load_weights calls model.post_load_weights() with no
arguments and relies on NextN subclasses to pin is_nextn=True, as
DeepseekV3ForCausalLMNextN does. Glm4MoeLiteForCausalLMNextN did not, so
the main model's branch ran on the draft and raised AttributeError:
'Glm4MoeLiteModelNextN' object has no attribute 'start_layer'. It hits
every weight-update session that writes without load_weights and covers
the draft (p2p on GLM-4.7-Flash with EAGLE).
GemmaRMSNorm's weight loader derives gemma_weight = weight + 1, which fused
allreduce kernels read. Loaders and weight-update sessions that write params
without load_weights (p2p, remote instance, sharded state, dummy) left it at
its old value.
post_load_weights now walks the model's modules to refresh gemma_weight,
which an instance made with __new__ alone does not have.
The post_load_weights util lives in loader.py, whose tests already have this
file and its fixtures; a separate file only adds an interpreter import to the
CPU gate.
post_load_weights refreshed GemmaRMSNorm's derived gemma_weight only in
sessions where no load ran. A `direct` or custom load writes params
without the norm's own loader yet marks the session loaded, so those
sessions kept the old gemma_weight.
@Zhichenzzz
Zhichenzzz added this pull request to stack #43938 October 12, 2026 23:55
@Zhichenzzz
Zhichenzzz merged commit 7f79d11 into sglang-miles Oct 12, 2026
69 of 78 checks passed
@Zhichenzzz
Zhichenzzz deleted the yueming/p2p-quant-fixes branch October 12, 2026 23:55
nv-hwoo pushed a commit to nv-hwoo/sglang that referenced this pull request Oct 20, 2026
…V budget, UE8M0 partial blocks, mxfp8 fused indexer, NVFP4 input scales (sgl-project#43251)
nanjiangwill pushed a commit to modal-projects/sglang that referenced this pull request Oct 21, 2026
…V budget, UE8M0 partial blocks, mxfp8 fused indexer, NVFP4 input scales (sgl-project#43251)

(cherry picked from commit 6ac15f4, partial: tests that need 56bef33's test files dropped)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDwTQn4gGFgVhzGXFs4aJx
Rockdu pushed a commit that referenced this pull request Oct 30, 2026
…V budget, UE8M0 partial blocks, mxfp8 fused indexer, NVFP4 input scales (#43251)
Rockdu pushed a commit that referenced this pull request Oct 30, 2026
…V budget, UE8M0 partial blocks, mxfp8 fused indexer, NVFP4 input scales (#43251)
Rockdu pushed a commit that referenced this pull request Oct 31, 2026
…V budget, UE8M0 partial blocks, mxfp8 fused indexer, NVFP4 input scales (#43251)
Rockdu pushed a commit that referenced this pull request Oct 31, 2026
…V budget, UE8M0 partial blocks, mxfp8 fused indexer, NVFP4 input scales (#43251)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blackwell SM100/SM120 deepseek quant LLM Quantization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants