[misc] Unify MLA scaling init and remove dead buffer / scaling code (#33363)

This commit is contained in:
Liangsheng Yin
2026-08-05 15:59:33 -07:00
committed by GitHub
parent 990a446773
commit c0ef548eef
7 changed files with 69 additions and 74 deletions
+14 -34
View File
@@ -855,21 +855,8 @@ class ModelConfig:
if is_deepseek_dsa(self.hf_text_config) if is_deepseek_dsa(self.hf_text_config)
else None else None
) )
# Handle rope scaling # In transformers v5, rope_scaling is just rope_parameters.
self.scaling = 1 / math.sqrt(self.qk_nope_head_dim + self.qk_rope_head_dim) self._init_mla_scaling(self.hf_text_config.rope_scaling)
# in transformers v5, rope_scaling is just rope_parameters for backward compatibility
rope_scaling = self.hf_text_config.rope_scaling
if rope_scaling:
# v5 uses "rope_type", v4 uses "type"
rope_type = (
rope_scaling.get("rope_type")
or rope_scaling.get("type")
or "default"
)
if rope_type != "default":
self.scaling = compute_mla_mscale_scaling(
rope_scaling, self.scaling
)
elif ( elif (
"DeepseekV4ForCausalLM" in self.hf_config.architectures "DeepseekV4ForCausalLM" in self.hf_config.architectures
or "DeepseekV4ForCausalLMNextN" in self.hf_config.architectures or "DeepseekV4ForCausalLMNextN" in self.hf_config.architectures
@@ -883,11 +870,7 @@ class ModelConfig:
self.index_head_dim = self.hf_config.index_head_dim self.index_head_dim = self.hf_config.index_head_dim
self.compress_ratios = self.hf_config.compress_ratios self.compress_ratios = self.hf_config.compress_ratios
self.attention_arch = AttentionArch.MHA self.attention_arch = AttentionArch.MHA
self.scaling = 1 / math.sqrt(self.qk_nope_head_dim + self.qk_rope_head_dim) self._init_mla_scaling(self.hf_config.rope_scaling)
if self.hf_config.rope_scaling:
self.scaling = compute_mla_mscale_scaling(
self.hf_config.rope_scaling, self.scaling
)
elif "Glm4MoeForCausalLMNextN" in self.hf_config.architectures: elif "Glm4MoeForCausalLMNextN" in self.hf_config.architectures:
if self.head_dim is None: if self.head_dim is None:
self.head_dim = ( self.head_dim = (
@@ -930,9 +913,7 @@ class ModelConfig:
self.qk_rope_head_dim = tc.qk_rope_head_dim self.qk_rope_head_dim = tc.qk_rope_head_dim
self.v_head_dim = tc.v_head_dim self.v_head_dim = tc.v_head_dim
self.qk_nope_head_dim = tc.qk_nope_head_dim self.qk_nope_head_dim = tc.qk_nope_head_dim
self.scaling = 1 / math.sqrt(self.qk_nope_head_dim + self.qk_rope_head_dim) self._init_mla_scaling(getattr(tc, "rope_scaling", None))
if getattr(tc, "rope_scaling", None):
self.scaling = compute_mla_mscale_scaling(tc.rope_scaling, self.scaling)
elif ( elif (
"BailingMoeV2_5ForCausalLM" in self.hf_config.architectures "BailingMoeV2_5ForCausalLM" in self.hf_config.architectures
or "BailingMoeForCausalLMNextN" in self.hf_config.architectures or "BailingMoeForCausalLMNextN" in self.hf_config.architectures
@@ -943,12 +924,7 @@ class ModelConfig:
self.qk_nope_head_dim = self.hf_text_config.qk_nope_head_dim self.qk_nope_head_dim = self.hf_text_config.qk_nope_head_dim
self.qk_rope_head_dim = self.hf_text_config.qk_rope_head_dim self.qk_rope_head_dim = self.hf_text_config.qk_rope_head_dim
self.v_head_dim = self.hf_config.v_head_dim self.v_head_dim = self.hf_config.v_head_dim
# Handle rope scaling with yarn self._init_mla_scaling(self.hf_config.rope_scaling)
self.scaling = 1 / math.sqrt(self.qk_nope_head_dim + self.qk_rope_head_dim)
if self.hf_config.rope_scaling:
self.scaling = compute_mla_mscale_scaling(
self.hf_config.rope_scaling, self.scaling
)
elif "SarvamMLAForCausalLM" in self.hf_config.architectures: elif "SarvamMLAForCausalLM" in self.hf_config.architectures:
self.head_dim = ( self.head_dim = (
self.hf_config.qk_nope_head_dim + self.hf_config.qk_rope_head_dim self.hf_config.qk_nope_head_dim + self.hf_config.qk_rope_head_dim
@@ -958,11 +934,7 @@ class ModelConfig:
self.qk_rope_head_dim = self.hf_config.qk_rope_head_dim self.qk_rope_head_dim = self.hf_config.qk_rope_head_dim
self.qk_nope_head_dim = self.hf_config.qk_nope_head_dim self.qk_nope_head_dim = self.hf_config.qk_nope_head_dim
self.v_head_dim = self.hf_config.v_head_dim self.v_head_dim = self.hf_config.v_head_dim
self.scaling = 1 / math.sqrt(self.qk_nope_head_dim + self.qk_rope_head_dim) self._init_mla_scaling(self.hf_config.rope_scaling)
if self.hf_config.rope_scaling:
self.scaling = compute_mla_mscale_scaling(
self.hf_config.rope_scaling, self.scaling
)
else: else:
if ( if (
"MistralModel" in self.hf_config.architectures "MistralModel" in self.hf_config.architectures
@@ -1039,6 +1011,12 @@ class ModelConfig:
if _hf_arch(self.hf_config) == "GlmImageForConditionalGeneration": if _hf_arch(self.hf_config) == "GlmImageForConditionalGeneration":
self.vocab_size = self.hf_text_config.vision_vocab_size self.vocab_size = self.hf_text_config.vision_vocab_size
def _init_mla_scaling(self, rope_scaling: Optional[dict]) -> None:
"""Base MLA attention scale from the head dims, then the rope mscale."""
self.scaling = 1 / math.sqrt(self.qk_nope_head_dim + self.qk_rope_head_dim)
if rope_scaling:
self.scaling = compute_mla_mscale_scaling(rope_scaling, self.scaling)
def get_total_num_attention_heads(self) -> int: def get_total_num_attention_heads(self) -> int:
return self.num_attention_heads return self.num_attention_heads
@@ -1964,7 +1942,9 @@ def compute_mla_mscale_scaling(rope_scaling: dict, base_scaling: float) -> float
Used by DeepSeek, BailingMoe, SarvamMLA and similar MLA models. Used by DeepSeek, BailingMoe, SarvamMLA and similar MLA models.
Transformers v5 also exposes the default RoPE parameters through Transformers v5 also exposes the default RoPE parameters through
``rope_scaling``. Those parameters do not request any scaling. ``rope_scaling``. Those parameters do not request any scaling.
Warns if 'factor' is missing from a scaling request (common in v5 configs).
""" """
# v5 uses "rope_type", v4 uses "type"
rope_type = rope_scaling.get("rope_type") or rope_scaling.get("type") rope_type = rope_scaling.get("rope_type") or rope_scaling.get("type")
if rope_type == "default": if rope_type == "default":
return base_scaling return base_scaling
@@ -77,7 +77,6 @@ class CutlassMLABackend(FlashInferMLAAttnBackend):
self.qk_nope_head_dim = model_runner.model_config.qk_nope_head_dim self.qk_nope_head_dim = model_runner.model_config.qk_nope_head_dim
self.qk_rope_head_dim = model_runner.model_config.qk_rope_head_dim self.qk_rope_head_dim = model_runner.model_config.qk_rope_head_dim
self.v_head_dim = model_runner.model_config.v_head_dim self.v_head_dim = model_runner.model_config.v_head_dim
self.scaling = model_runner.model_config.scaling
self.data_type = model_runner.kv_cache_dtype self.data_type = model_runner.kv_cache_dtype
self.q_data_type = model_runner.dtype self.q_data_type = model_runner.dtype
self.kv_cache_dim = self.kv_lora_rank + self.qk_rope_head_dim self.kv_cache_dim = self.kv_lora_rank + self.qk_rope_head_dim
@@ -85,7 +85,6 @@ class FlashMLABackend(FlashInferMLAAttnBackend):
self.qk_nope_head_dim = model_runner.model_config.qk_nope_head_dim self.qk_nope_head_dim = model_runner.model_config.qk_nope_head_dim
self.qk_rope_head_dim = model_runner.model_config.qk_rope_head_dim self.qk_rope_head_dim = model_runner.model_config.qk_rope_head_dim
self.v_head_dim = model_runner.model_config.v_head_dim self.v_head_dim = model_runner.model_config.v_head_dim
self.scaling = model_runner.model_config.scaling
self.data_type = model_runner.kv_cache_dtype self.data_type = model_runner.kv_cache_dtype
self.q_data_type = model_runner.dtype self.q_data_type = model_runner.dtype
self.kv_cache_dim = self.kv_lora_rank + self.qk_rope_head_dim self.kv_cache_dim = self.kv_lora_rank + self.qk_rope_head_dim
@@ -223,7 +223,6 @@ class TRTLLMMLABackend(FlashInferMLAAttnBackend):
# Runtime parameters # Runtime parameters
self.backend = backend self.backend = backend
self.scaling = config.scaling
self.data_type = model_runner.kv_cache_dtype self.data_type = model_runner.kv_cache_dtype
self.q_data_type = model_runner.dtype self.q_data_type = model_runner.dtype
self.page_size = model_runner.page_size self.page_size = model_runner.page_size
@@ -32,7 +32,10 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple
import torch import torch
from sglang.srt.model_executor.input_buffers import share_input_buffer from sglang.srt.model_executor.input_buffers import (
INDEX_SEMANTIC_BUFFERS,
share_input_buffer,
)
if TYPE_CHECKING: if TYPE_CHECKING:
from sglang.srt.model_executor.forward_batch_info import ForwardBatch from sglang.srt.model_executor.forward_batch_info import ForwardBatch
@@ -775,6 +778,22 @@ def build_decode_registry(
bind=canary, bind=canary,
) )
# ZERO covers replay; a mid-serving recapture is covered by
# ForwardInputBuffers.reset_index_buffers, which keys off
# INDEX_SEMANTIC_BUFFERS. Diverge and one of the two skips a buffer.
registered = set(reg.slot_names())
zero_slots = {
name
for name in registered
if reg.get_slot(name).padding_policy is PaddingPolicy.ZERO
}
expected_zero = INDEX_SEMANTIC_BUFFERS & registered
assert zero_slots == expected_zero, (
"ZERO-policy slots and INDEX_SEMANTIC_BUFFERS disagree: "
f"zero_but_unlisted={sorted(zero_slots - expected_zero)}, "
f"listed_but_not_zero={sorted(expected_zero - zero_slots)}"
)
return reg return reg
@@ -41,34 +41,10 @@ def share_input_buffer(name: str, new_buffer: torch.Tensor) -> torch.Tensor:
return canonical.as_strided(new_buffer.size(), new_buffer.stride()) return canonical.as_strided(new_buffer.size(), new_buffer.stride())
def share_input_buffers_in(obj) -> None:
"""Pool every tensor buffer on ``obj`` (dataclass / ``SimpleNamespace``)
through the process-wide pool, in place. No-op on NPU; recurses into dict /
dataclass buffer fields (``pp_proxy_tensors`` / ``ngram_embedding_info``)."""
if is_npu():
return
for name, buffer in list(vars(obj).items()):
if buffer is None:
continue
if dataclasses.is_dataclass(buffer):
buffer = vars(buffer)
if isinstance(buffer, dict):
for sub_name, sub_buffer in buffer.items():
assert isinstance(
sub_buffer, torch.Tensor
), f"Field {name}.{sub_name} is expected to be a torch.Tensor, but got {type(sub_buffer)}."
buffer[sub_name] = share_input_buffer(f"{name}.{sub_name}", sub_buffer)
else:
assert isinstance(
buffer, torch.Tensor
), f"Field {name} is expected to be a torch.Tensor, a dict of torch.Tensor, or a dataclass of torch.Tensor, but got {type(buffer)}."
setattr(obj, name, share_input_buffer(name, buffer))
# Values that index the rope table, the KV pool, req_to_token, or the mamba # Values that index the rope table, the KV pool, req_to_token, or the mamba
# state pool, so stale content is unsafe to execute. # state pool, so stale content is unsafe to execute. build_decode_registry
_INDEX_SEMANTIC_BUFFERS = frozenset( # asserts its ZERO-policy slots against this set.
INDEX_SEMANTIC_BUFFERS = frozenset(
{ {
"positions", "positions",
"mrope_positions", "mrope_positions",
@@ -83,13 +59,10 @@ _INDEX_SEMANTIC_BUFFERS = frozenset(
@dataclass @dataclass
class ForwardInputBuffers: class ForwardInputBuffers:
def _share_one_buffer(self, name: str, new_buffer: torch.Tensor) -> torch.Tensor:
return share_input_buffer(name, new_buffer)
def reset_index_buffers(self) -> None: def reset_index_buffers(self) -> None:
"""Zero the index-semantic buffers this set declares.""" """Zero the index-semantic buffers this set declares."""
for f in fields(self): for f in fields(self):
if f.name not in _INDEX_SEMANTIC_BUFFERS: if f.name not in INDEX_SEMANTIC_BUFFERS:
continue continue
buffer = getattr(self, f.name) buffer = getattr(self, f.name)
if buffer is not None: if buffer is not None:
@@ -115,13 +88,11 @@ class ForwardInputBuffers:
assert isinstance( assert isinstance(
sub_buffer, torch.Tensor sub_buffer, torch.Tensor
), f"Field {name}.{sub_name} is expected to be a torch.Tensor, but got {type(sub_buffer)}." ), f"Field {name}.{sub_name} is expected to be a torch.Tensor, but got {type(sub_buffer)}."
new_buffer = self._share_one_buffer( buffer[sub_name] = share_input_buffer(
f"{name}.{sub_name}", sub_buffer f"{name}.{sub_name}", sub_buffer
) )
buffer[sub_name] = new_buffer
else: else:
assert isinstance( assert isinstance(
buffer, torch.Tensor buffer, torch.Tensor
), f"Field {name} is expected to be a torch.Tensor, a dict of torch.Tensor, or a dataclass of torch.Tensor, but got {type(buffer)}." ), f"Field {name} is expected to be a torch.Tensor, a dict of torch.Tensor, or a dataclass of torch.Tensor, but got {type(buffer)}."
new_buffer = self._share_one_buffer(name, buffer) setattr(self, name, share_input_buffer(name, buffer))
setattr(self, name, new_buffer)
@@ -1,13 +1,22 @@
import math import math
import unittest import unittest
from sglang.srt.configs.model_config import compute_mla_mscale_scaling from sglang.srt.configs.model_config import ModelConfig, compute_mla_mscale_scaling
from sglang.test.ci.ci_register import register_cpu_ci from sglang.test.ci.ci_register import register_cpu_ci
from sglang.test.test_utils import CustomTestCase from sglang.test.test_utils import CustomTestCase
register_cpu_ci(est_time=1, suite="base-a-test-cpu") register_cpu_ci(est_time=1, suite="base-a-test-cpu")
def _mla_scaling(rope_scaling) -> tuple[float, float]:
"""Run ModelConfig._init_mla_scaling on fixed head dims -> (base, result)."""
config = ModelConfig.__new__(ModelConfig)
config.qk_nope_head_dim = 128
config.qk_rope_head_dim = 64
config._init_mla_scaling(rope_scaling)
return 1 / math.sqrt(192), config.scaling
class TestMlaMscaleScaling(CustomTestCase): class TestMlaMscaleScaling(CustomTestCase):
def test_ignores_transformers_v5_default_rope_parameters(self): def test_ignores_transformers_v5_default_rope_parameters(self):
base_scaling = 1 / math.sqrt(72) base_scaling = 1 / math.sqrt(72)
@@ -65,5 +74,24 @@ class TestMlaMscaleScaling(CustomTestCase):
) )
class TestInitMlaScaling(CustomTestCase):
"""ModelConfig._init_mla_scaling must not re-add a "default" fallback of its
own: DeepseekV2AttentionMLA stamps rope_type="deepseek_yarn" on any non-empty
rope_scaling, so a dict without rope_type/type still gets a yarn rope and its
mscale belongs in the scale FlashInferMLA reads as sm_scale."""
def test_applies_mscale_without_rope_type(self):
base, scaling = _mla_scaling({"factor": 40, "mscale_all_dim": 1})
self.assertGreater(scaling, base)
def test_ignores_default_rope_type(self):
base, scaling = _mla_scaling({"rope_type": "default", "factor": 40})
self.assertEqual(scaling, base)
def test_no_rope_scaling_keeps_base(self):
base, scaling = _mla_scaling(None)
self.assertEqual(scaling, base)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()