Files
vllm/tests/models/test_dspark_mla.py
Wentao YeGitHubmergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
56a4b63d44 [K3 Perf] Optimize k3 dspark fused kv, 4.5~4.6x kernel performance improvement (#50585)
Signed-off-by: yewentao256 <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2026-08-07 12:16:11 -07:00

191 lines
6.2 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
from types import SimpleNamespace
import pytest
import torch
import torch.nn as nn
from vllm.compilation.wrapper import TorchCompileWithNoGuardsWrapper
from vllm.model_executor.layers.logits_processor import LogitsProcessor
from vllm.model_executor.models.qwen3_dspark import DSparkMarkovHead
from vllm.model_executor.models.registry import ModelRegistry
from vllm.models.kimi_k3.nvidia import dspark_mla
from vllm.models.kimi_k3.nvidia.dspark_mla import K3DSparkForCausalLM, K3DSparkModel
def test_dspark_mla_uses_compile_free_model_entrypoint():
assert ModelRegistry._try_load_model_cls("K3DSparkModel") is K3DSparkForCausalLM
assert not issubclass(K3DSparkModel, TorchCompileWithNoGuardsWrapper)
@pytest.mark.parametrize(
("checkpoint_name", "runtime_name", "shard_id"),
[
(
"layers.0.self_attn.q_a_proj.weight",
"model.layers.0.self_attn.fused_qkv_a_proj.weight",
0,
),
(
"layers.0.self_attn.kv_a_proj_with_mqa.weight",
"model.layers.0.self_attn.fused_qkv_a_proj.weight",
1,
),
(
"layers.0.mlp.gate_proj.weight",
"model.layers.0.mlp.gate_up_proj.weight",
0,
),
(
"layers.0.mlp.up_proj.weight",
"model.layers.0.mlp.gate_up_proj.weight",
1,
),
("context_proj.weight", "model.context_proj.weight", None),
],
)
def test_dspark_mla_checkpoint_weight_mapping(checkpoint_name, runtime_name, shard_id):
assert K3DSparkForCausalLM.hf_to_vllm_mapper._map_name_with_shard(
checkpoint_name
) == (runtime_name, shard_id)
def test_dspark_mla_shares_frozen_target_weights_and_skips_training_head():
assert not K3DSparkForCausalLM.has_own_embed_tokens
assert not K3DSparkForCausalLM.has_own_lm_head
assert set(K3DSparkForCausalLM.checkpoint_skip_substrs) == {
"confidence_head",
"embed_tokens",
"lm_head",
}
@pytest.mark.cpu_test
def test_dspark_markov_head_is_replicated(
monkeypatch: pytest.MonkeyPatch,
):
from vllm.model_executor.layers import logits_processor, vocab_parallel_embedding
monkeypatch.setattr(
vocab_parallel_embedding, "get_tensor_model_parallel_rank", lambda: 3
)
monkeypatch.setattr(
vocab_parallel_embedding,
"get_tensor_model_parallel_world_size",
lambda: 8,
)
monkeypatch.setattr(
logits_processor,
"get_current_vllm_config",
lambda: SimpleNamespace(model_config=None),
)
head = DSparkMarkovHead(128, 128, 8, prefix="markov_head")
assert head.markov_w2.tp_size == 1
assert head.markov_w1.weight.shape == (128, 8)
assert head.markov_w2.weight.shape == (128, 8)
def fail_collective(*args, **kwargs):
raise AssertionError("replicated Markov head must not invoke TP collectives")
monkeypatch.setattr(
vocab_parallel_embedding,
"tensor_model_parallel_all_reduce",
fail_collective,
)
logits_processor = LogitsProcessor(128)
monkeypatch.setattr(logits_processor, "_gather_logits", fail_collective)
markov_embed = head.embed(torch.tensor([1, 2]))
bias = head.bias(markov_embed, logits_processor)
assert markov_embed.shape == (2, 8)
assert bias.shape == (2, 128)
@pytest.mark.cpu_test
def test_k3_dspark_uses_replicated_markov_head(monkeypatch: pytest.MonkeyPatch):
markov_head_calls = []
context_kv_proj_calls = []
class DummyModule(nn.Module):
def __init__(self, *args, **kwargs):
super().__init__()
def make_markov_head(*args, **kwargs):
markov_head_calls.append((args, kwargs))
return DummyModule()
def make_context_kv_proj(*args, **kwargs):
context_kv_proj_calls.append((args, kwargs))
return DummyModule()
monkeypatch.setattr(dspark_mla, "get_draft_quant_config", lambda _: None)
monkeypatch.setattr(dspark_mla, "ReplicatedLinear", DummyModule)
monkeypatch.setattr(dspark_mla, "MergedColumnParallelLinear", make_context_kv_proj)
monkeypatch.setattr(dspark_mla, "RMSNorm", DummyModule)
monkeypatch.setattr(dspark_mla, "K3DSparkDecoderLayer", DummyModule)
monkeypatch.setattr(dspark_mla, "DSparkMarkovHead", make_markov_head)
config = SimpleNamespace(
target_hidden_size=16,
num_target_layers=2,
hidden_size=8,
kv_lora_rank=3,
qk_rope_head_dim=1,
rms_norm_eps=1e-6,
num_hidden_layers=1,
vocab_size=128,
draft_vocab_size=128,
markov_rank=4,
)
vllm_config = SimpleNamespace(
speculative_config=SimpleNamespace(
draft_model_config=SimpleNamespace(hf_config=config)
),
scheduler_config=SimpleNamespace(max_num_batched_tokens=16),
)
K3DSparkModel(vllm_config=vllm_config, start_layer_id=0, prefix="model")
assert len(markov_head_calls) == 1
assert context_kv_proj_calls == [
(
(8, [4]),
{
"bias": False,
"return_bias": False,
"quant_config": None,
"prefix": "model.layers.0.self_attn.fused_qkv_a_proj",
"disable_tp": True,
},
)
]
def test_context_kv_weights_are_loaded_as_merged_linear_shards():
weights = [
(
"layers.0.self_attn.kv_a_proj_with_mqa.weight_packed",
torch.arange(4),
),
(
"layers.1.self_attn.kv_a_proj_with_mqa.weight_scale",
torch.tensor(0.5),
),
]
duplicated = dspark_mla._duplicate_context_kv_weights(weights, 2)
mapped = list(K3DSparkForCausalLM.hf_to_vllm_mapper.apply(duplicated))
assert [name for name, _ in mapped] == [
"model.layers.0.self_attn.fused_qkv_a_proj.weight_packed",
"model.context_kv_proj.weight_packed",
"model.layers.1.self_attn.fused_qkv_a_proj.weight_scale",
"model.context_kv_proj.weight_scale",
]
assert [weight.shard_id for _, weight in mapped] == [1, 0, 1, 1]
assert mapped[0][1].data_ptr() == mapped[1][1].data_ptr()
assert mapped[2][1].data_ptr() == mapped[3][1].data_ptr()