Skip to content

glm52: free dead kv_b pack post-absorb + share draft rope tables (stacks on #145)#146

Closed
MadeBy561 wants to merge 2 commits into
local-inference-lab:dev/gilded-gnosisfrom
MadeBy561:feat/glm52-draft-rope-share-kvb-free
Closed

glm52: free dead kv_b pack post-absorb + share draft rope tables (stacks on #145)#146
MadeBy561 wants to merge 2 commits into
local-inference-lab:dev/gilded-gnosisfrom
MadeBy561:feat/glm52-draft-rope-share-kvb-free

Conversation

@MadeBy561

@MadeBy561 MadeBy561 commented Jul 20, 2026

Copy link
Copy Markdown

Two KLD-neutral VRAM reclaims for the GLM-5.2 serving stack. Branch includes #145 (nvfp4 outer scales) as its base commit; review the tip commit only.

kv_b dead pack (−290 MiB/GPU). With no MHA prefill backend (prefill_backend is None — the B12X_MLA_SPARSE shape), decode and extend both run the MQA lane on the absorbed W_UK_T/W_UV pair; kv_b_proj's quantized weight is only ever read by the absorb dequant in process_weights_after_loading. Free the pack right there (bf16-posture guarded, load-time only — runtime frees of this tensor are unsafe while a kernel launch may hold its storage).

Draft rope-table duplication (−128 MiB/GPU). Draft ModelConfigs never receive the target's dict hf_overrides, so a target that shrinks max_position_embeddings (600k here vs a 1M checkpoint default) leaves the draft allocating full-length bf16 cos/sin tables. Propagating the smaller bound is exact: scheduling is limited by the target's max.

Validated on 4×96GB TP4+DCP4 (nvfp4_ds_mla + fp8 rope + #145 scales): KV pool 600,255 → 700,255 tokens at the same pin arithmetic headroom, teacher-forced prefill KLD 0.1360 ± 0.0019 over 5 fresh boots (cert band 0.1356 ± 0.0054), 157,932-token prefill 67 s and decode 104-108 t/s unchanged, MTP acceptance 94.1% unchanged.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added configurable per-layer outer scaling for GLM-5.2 NVFP4 MLA KV caches.
    • Included calibration data and usage documentation, benchmark results, and cache invalidation guidance.
    • Added an environment setting to enable or disable MLA KV outer scales.
  • Bug Fixes

    • Draft models now respect the target model’s maximum context length.
    • Reduced unnecessary memory usage when MLA prefill support is unavailable.

MadeBy561 and others added 2 commits July 20, 2026 15:02
Per-layer outer-scale calibration for nvfp4_ds_mla KV
(VLLM_NVFP4_MLA_SCALES_FILE, format v1) with an explicit default-off knob
in serve-glm52.sh. Collapses the NVFP4-vs-FP8 KV KLD gap to ~+0.008-0.009
(0.1345/0.1356 vs 0.1263, 5 fresh boots each, rtx6kpro protocol) while
raising max context from 373k to 550k/600k+ on 4x96GB.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With no MHA prefill backend the MLA runtime consumes only the absorbed
W_UK_T/W_UV pair, so kv_b_proj's quantized pack is dead after the absorb
dequant: free it (~290 MiB/GPU at GLM-5.2 TP4). Draft configs never
receive the target's dict hf_overrides, so a shrunken target
max_position_embeddings left the draft allocating duplicate full-length
rotary tables (128 MiB/GPU): propagate the smaller bound, which is exact
because scheduling is limited by the target.

Validated on GLM-5.2 4x96GB TP4+DCP4: KV pool 600,255 -> 700,255 tokens
at unchanged KLD (5 fresh-boot runs 0.1360+-0.0019 vs 0.1356+-0.0054
cert), 157,932-token prefill and decode throughput unchanged, MTP accept
rate 94.1% unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

👋 Hi! Thank you for contributing to the vLLM project.

💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in #pr-reviews, coordinate on features in #feat- channels, or join special interest groups in #sig- channels.

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 ready label to the PR or enable auto-merge.

If you have any questions, please reach out to us on Slack at https://slack.vllm.ai.

Agent Guidelines

IMPORTANT: 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.

🚀

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds GLM-5.2 NVFP4 MLA outer-scale documentation, calibration data, serving configuration, and attention cleanup. It also clamps speculative draft position limits to the target model’s limit.

Changes

NVFP4 MLA outer scales

Layer / File(s) Summary
Scale artifact and serving configuration
kv-scales/*, serve-glm52.sh
Adds the GLM-5.2 outer-scale JSON data, usage and cache-invalidation documentation, and the VLLM_NVFP4_MLA_SCALES_FILE environment variable.
MLA quantization-state cleanup
vllm/model_executor/layers/attention/mla_attention.py
Clears unused B12X packed weights and resets KV projection parameters when no MLA prefill backend is configured.

Speculative decoding position limit

Layer / File(s) Summary
Draft position-limit clamping
vllm/config/speculative.py
Clamps the draft configuration’s max_position_embeddings to the target model’s smaller value when the configurations are distinct.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: lukealonso, voipmonitor

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main GLM-5.2 VRAM reclamation changes: freeing dead kv_b packs and sharing draft rope tables.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@vllm/config/speculative.py`:
- Around line 965-972: Update the max_position_embeddings lookup in the
speculative configuration clamp to read from
self.target_model_config.hf_text_config and
self.draft_model_config.hf_text_config instead of the top-level hf_config
objects. Preserve the existing None checks, identity comparison, and
target-versus-draft limit comparison.

In `@vllm/model_executor/layers/attention/mla_attention.py`:
- Around line 1379-1381: Update the parameter-emptying logic in the shown block
to avoid all param.data access: inspect the parameter tensor’s numel and dtype
directly, then use PyTorch’s native set() method to replace its underlying
storage with an empty tensor of the appropriate type. Preserve the existing
None, non-empty, and non-bfloat16 conditions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4abc7269-6e66-4edd-8f31-200afe78deb8

📥 Commits

Reviewing files that changed from the base of the PR and between 371085e and 856632c.

📒 Files selected for processing (5)
  • kv-scales/README.md
  • kv-scales/glm52-nvfp4-nf3-hybrid_mla_outer_scales_v1.json
  • serve-glm52.sh
  • vllm/config/speculative.py
  • vllm/model_executor/layers/attention/mla_attention.py

Comment on lines +965 to +972
target_mpe = getattr(
self.target_model_config.hf_config,
"max_position_embeddings", None)
draft_hf = self.draft_model_config.hf_config
draft_mpe = getattr(draft_hf, "max_position_embeddings", None)
if (target_mpe is not None and draft_mpe is not None
and draft_hf is not self.target_model_config.hf_config
and target_mpe < draft_mpe):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant section of vllm/config/speculative.py
wc -l vllm/config/speculative.py
sed -n '920,1010p' vllm/config/speculative.py

# Find related text-config accessors/usages in the repo
rg -n "hf_text_config|get_text_config\(\)|max_position_embeddings" vllm/config -S

# Show where hf_text_config is defined/used on the config objects
rg -n "class .*Config|def .*hf_text_config|`@property`.*hf_text_config" vllm -S

Repository: local-inference-lab/vllm

Length of output: 50381


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- speculative.py around the target block ---'
sed -n '940,980p' vllm/config/speculative.py

echo '--- hf_text_config references in vllm/config/*.py ---'
rg -n --glob 'vllm/config/*.py' 'hf_text_config|get_text_config\(\)|max_position_embeddings' vllm/config

echo '--- likely config property definitions ---'
rg -n --glob 'vllm/config/*.py' 'def hf_text_config|`@property`\s+def hf_text_config|class .*Config' vllm/config

Repository: local-inference-lab/vllm

Length of output: 7893


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- vllm/config/model.py: hf_text_config field and init ---'
sed -n '130,190p' vllm/config/model.py
sed -n '540,590p' vllm/config/model.py

echo '--- vllm/transformers_utils/config.py: get_hf_text_config ---'
rg -n "def get_hf_text_config|class .*Config" vllm/transformers_utils/config.py
sed -n '1,220p' vllm/transformers_utils/config.py

echo '--- speculative.py: later draft hf_text_config assignment ---'
sed -n '1270,1310p' vllm/config/speculative.py

Repository: local-inference-lab/vllm

Length of output: 15528


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- get_hf_text_config definition ---'
sed -n '1040,1125p' vllm/transformers_utils/config.py

echo '--- model config helpers around LongRoPE / max_position_embeddings ---'
sed -n '2200,2275p' vllm/config/model.py

echo '--- where get_text_config() is used in the repo ---'
rg -n "get_text_config\(\)\.max_position_embeddings|get_text_config\(\)|original_max_position_embeddings" vllm -S

Repository: local-inference-lab/vllm

Length of output: 18153


Read max_position_embeddings from the text sub-config. hf_config.max_position_embeddings is unset on nested multimodal wrappers, so this clamp can miss the real limit and leave the draft at the checkpoint default. Use self.target_model_config.hf_text_config and self.draft_model_config.hf_text_config here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@vllm/config/speculative.py` around lines 965 - 972, Update the
max_position_embeddings lookup in the speculative configuration clamp to read
from self.target_model_config.hf_text_config and
self.draft_model_config.hf_text_config instead of the top-level hf_config
objects. Preserve the existing None checks, identity comparison, and
target-versus-draft limit comparison.

Comment on lines +1379 to +1381
if (param is not None and param.data.numel() > 0
and param.data.dtype != torch.bfloat16):
param.data = param.data.new_empty((0,))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Avoid .data access on parameters to prevent runtime crashes.

Accessing the .data attribute on tensor-parallel parameters will cause a deterministic runtime crash. As per the provided codebase context, the .data property on SharedWeightParameter is intentionally disabled and explicitly raises a ValueError when accessed.

You can safely check the tensor's properties directly and use PyTorch's native .set_() method to overwrite its underlying storage to an empty tensor without invoking the .data property getter or setter.

🐛 Proposed fix
-                if (param is not None and param.data.numel() > 0
-                        and param.data.dtype != torch.bfloat16):
-                    param.data = param.data.new_empty((0,))
+                if (param is not None and param.numel() > 0
+                        and param.dtype != torch.bfloat16):
+                    param.set_(param.new_empty(0))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (param is not None and param.data.numel() > 0
and param.data.dtype != torch.bfloat16):
param.data = param.data.new_empty((0,))
if (param is not None and param.numel() > 0
and param.dtype != torch.bfloat16):
param.set_(param.new_empty(0))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@vllm/model_executor/layers/attention/mla_attention.py` around lines 1379 -
1381, Update the parameter-emptying logic in the shown block to avoid all
param.data access: inspect the parameter tensor’s numel and dtype directly, then
use PyTorch’s native set() method to replace its underlying storage with an
empty tensor of the appropriate type. Preserve the existing None, non-empty, and
non-bfloat16 conditions.

@voipmonitor

Copy link
Copy Markdown

Superseded by two independent PRs, both based directly on the current
dev/gilded-gnosis head:

This PR combined unrelated lifecycle and configuration changes, was stacked on
#145, and its two CodeRabbit findings exposed real scope/correctness issues. The
replacement PRs are direct-GG, independently reviewable, non-draft, and include
the relevant unit and reload coverage. Closing this PR to keep one canonical
implementation of each optimization.

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.

2 participants