[ROCm] [BugFix] Fix Quark GLM-5.2 Checkpoint inference: indexer wk per-channel FP8 dequant + missing sparse-MLA metadata fields (#48886)

Signed-off-by: Colin Zeng <Colin.Zeng@amd.com>
Signed-off-by: ColinZ22 <Colin.Zeng@amd.com>
Co-authored-by: TJian <tunjian.tan@embeddedllm.com>
Co-authored-by: fanxingran <xingran.fan@amd.com>
This commit is contained in:
Colin Z
2026-07-27 22:38:22 +00:00
committed by GitHub
co-authored by TJian fanxingran
parent 53f6dd5c6f
commit 28158b2fc3
5 changed files with 129 additions and 21 deletions
@@ -88,6 +88,81 @@ def _make_common_metadata():
)
def _make_mixed_common_metadata():
# req0: query_len 1 (decode), req1: query_len 4 (prefill) -> decode-first
query_start_loc = torch.tensor([0, 1, 5], dtype=torch.int32, device="cpu")
seq_lens = torch.tensor([16, 10], dtype=torch.int32, device="cpu")
return CommonAttentionMetadata(
query_start_loc=query_start_loc,
query_start_loc_cpu=query_start_loc,
seq_lens=seq_lens,
_seq_lens_cpu=seq_lens,
num_reqs=2,
num_actual_tokens=5,
max_query_len=4,
max_seq_len=16,
block_table_tensor=torch.arange(16, dtype=torch.int32, device="cpu").view(2, 8),
slot_mapping=torch.arange(5, dtype=torch.int64, device="cpu"),
)
def _patch_build_deps(monkeypatch, events=None):
"""Stub the aiter kernel, triton helper and CUDA sync so ``build()`` runs
on CPU."""
def fake_generate_sparse_seqlen_triton(
query_lens, seq_lens, cu_query_lens, topk_token, num_tokens, max_query_len
):
return torch.zeros(num_tokens, dtype=torch.int32, device="cpu")
fake_aiter = _FakeAiter("aiter")
fake_aiter.get_mla_metadata_v1 = Mock(side_effect=lambda *a, **k: None)
monkeypatch.setitem(sys.modules, "aiter", fake_aiter)
monkeypatch.setattr(
sparse_mod, "generate_sparse_seqlen_triton", fake_generate_sparse_seqlen_triton
)
monkeypatch.setattr(
sparse_mod.torch.cuda,
"current_stream",
lambda device=None: SimpleNamespace(
synchronize=lambda: events.append("sync") if events is not None else None
),
)
def test_build_populates_decode_only_split_fields(monkeypatch):
"""Decode-only batch: all reqs count as decodes, prefill fields default."""
builder = _make_builder()
_patch_build_deps(monkeypatch)
md = builder.build(
common_prefix_len=0, common_attn_metadata=_make_common_metadata()
)
assert md.num_decodes == 2
assert md.num_prefills == 0
assert md.num_decode_tokens == 2
assert md.prefill_max_seq_len == 0
assert md.prefill is None
def test_build_populates_mixed_split_fields(monkeypatch):
"""Mixed decode+prefill batch: split is reported, prefill fields stay
default because this impl always runs the MQA path."""
builder = _make_builder()
_patch_build_deps(monkeypatch)
md = builder.build(
common_prefix_len=0, common_attn_metadata=_make_mixed_common_metadata()
)
assert md.num_decodes == 1
assert md.num_prefills == 1
assert md.num_decode_tokens == 1
assert md.prefill_max_seq_len == 0
assert md.prefill is None
def test_sparse_persistent_metadata_syncs_only_after_recompute(monkeypatch):
builder = _make_builder()
common_metadata = _make_common_metadata()
@@ -515,29 +515,36 @@ class MLAAttention(nn.Module, AttentionLayerBase):
compilation_config.static_forward_context[prefix] = self
self.prefill_backend: MLAPrefillBackend | None
try:
prefill_backend_cls = get_mla_prefill_backend(vllm_config)
except ValueError:
if (
not self.impl.is_sparse
or vllm_config.attention_config.mla_prefill_backend is not None
):
raise
if self.impl.is_sparse and not self.impl.supports_dense_mha_prefill:
logger.warning_once(
"No MLA prefill backend supports this model; sparse MLA will use the "
"top-k MQA path only (no dense-MHA prefill)."
"Sparse MLA impl has no dense-MHA prefill path; using the top-k "
"MQA path only."
)
self.prefill_backend = None
else:
self.prefill_backend = prefill_backend_cls(
num_heads=self.num_heads,
scale=self.scale,
kv_lora_rank=self.kv_lora_rank,
qk_nope_head_dim=self.qk_nope_head_dim,
qk_rope_head_dim=self.qk_rope_head_dim,
v_head_dim=self.v_head_dim,
vllm_config=vllm_config,
)
try:
prefill_backend_cls = get_mla_prefill_backend(vllm_config)
except ValueError:
if (
not self.impl.is_sparse
or vllm_config.attention_config.mla_prefill_backend is not None
):
raise
logger.warning_once(
"No MLA prefill backend supports this model; sparse MLA will "
"use the top-k MQA path only (no dense-MHA prefill)."
)
self.prefill_backend = None
else:
self.prefill_backend = prefill_backend_cls(
num_heads=self.num_heads,
scale=self.scale,
kv_lora_rank=self.kv_lora_rank,
qk_nope_head_dim=self.qk_nope_head_dim,
qk_rope_head_dim=self.qk_rope_head_dim,
v_head_dim=self.v_head_dim,
vllm_config=vllm_config,
)
self.kv_cache = torch.tensor([])
+7 -2
View File
@@ -851,11 +851,16 @@ def _try_load_fp8_indexer_wk(
# We have both weight and scale: dequantize FP8 to BF16.
weight_fp8, scale_inv = entry["weight"], entry["scale"]
del buf[layer_prefix]
block_size = weight_fp8.shape[1] // scale_inv.shape[1]
if scale_inv.ndim == 1:
# Per-channel scale: one scale per row of [out, in]
group_shape = GroupShape(1, weight_fp8.shape[1])
else:
block_size = weight_fp8.shape[1] // scale_inv.shape[1]
group_shape = GroupShape(block_size, block_size)
weight_bf16 = scaled_dequantize(
weight_fp8,
scale_inv,
group_shape=GroupShape(block_size, block_size),
group_shape=group_shape,
out_dtype=torch.bfloat16,
)
+4
View File
@@ -803,6 +803,10 @@ class AttentionImplBase(ABC, Generic[T]):
# route between the dense-MHA prefill and sparse-MQA paths.
is_sparse: ClassVar[bool] = False
# Whether this impl provides a dense-MHA prefill path (forward_mha). Sparse
# impls without one run the top-k MQA path for all requests.
supports_dense_mha_prefill: ClassVar[bool] = True
# Required attributes that all impls should have
num_heads: int
head_size: int
@@ -30,6 +30,7 @@ from vllm.v1.attention.backend import (
from vllm.v1.attention.backends.mla.rocm_aiter_mla import (
AiterMLAHelper,
)
from vllm.v1.attention.backends.utils import split_decodes_and_prefills
from vllm.v1.kv_cache_interface import AttentionSpec
from vllm.v1.worker.workspace import current_workspace_manager
@@ -333,6 +334,14 @@ class ROCMAiterMLASparseMetadata(AttentionMetadata):
block_size: int = 1
topk_tokens: int = 2048
# Fields read by the shared MLA forward. This impl has no dense-MHA prefill
# path (supports_dense_mha_prefill=False), so it always runs the MQA path;
num_decodes: int = 0
num_prefills: int = 0
num_decode_tokens: int = 0
prefill_max_seq_len: int = 0
prefill: object = None
# Persistent MLA metadata (only populated when persistent mode is enabled,
# i.e. when the aiter sparse decode kernel supports work-stealing splits).
work_meta_data: torch.Tensor | None = None
@@ -488,6 +497,10 @@ class ROCMAiterMLASparseMetadataBuilder(
fast_build: bool = False,
) -> ROCMAiterMLASparseMetadata:
num_tokens = common_attn_metadata.num_actual_tokens
(num_decodes, num_prefills, num_decode_tokens, _) = split_decodes_and_prefills(
common_attn_metadata,
decode_threshold=self.reorder_batch_threshold or 1,
)
starts = np.asarray(common_attn_metadata.query_start_loc_cpu, dtype=np.int32)
seg_lengths = np.diff(starts)
req_id_per_token = np.repeat(
@@ -604,6 +617,9 @@ class ROCMAiterMLASparseMetadataBuilder(
block_size=self.kv_cache_spec.block_size,
attn_out_dtype=self.model_dtype,
topk_tokens=self.topk_tokens,
num_decodes=num_decodes,
num_prefills=num_prefills,
num_decode_tokens=num_decode_tokens,
qo_indptr=qo_indptr,
paged_kv_last_page_len=paged_kv_last_page_len,
paged_kv_indices=paged_kv_indices,
@@ -649,6 +665,7 @@ def reference_mla_sparse_prefill(
class ROCMAiterMLASparseImpl(MLAAttentionImpl[ROCMAiterMLASparseMetadata]):
is_sparse = True
supports_dense_mha_prefill = False
def __init__(
self,