Files
wehub-resource-sync 59a0a3844c
PR Test AMD / cancel-on-close (push) Has been skipped
PR Test NVIDIA ARM / scan (push) Has been skipped
PR Test NVIDIA / cancel-on-close (push) Has been skipped
PR Test AMD / scan (push) Has been skipped
PR Test NVIDIA ARM / cancel-on-close (push) Has been skipped
PR Test NVIDIA / scan (push) Has been skipped
Release Docker Images / build (cu129-torch-2.11.0) (push) Has been skipped
Release Docker Images / build (cu130-torch-2.11.0) (push) Has been skipped
Release PyPI / publish (push) Has been skipped
Scheduler Python Test / test (push) Successful in 27m19s
Docs / build (push) Successful in 28m8s
Scheduler C++ Test / test (push) Successful in 28m19s
Scheduler C++ Test / test-flat (push) Successful in 28m18s
Docs / deploy (push) Has been cancelled
PR Test AMD / finish (push) Has been cancelled
PR Test NVIDIA / finish (push) Has been cancelled
PR Test NVIDIA ARM / finish (push) Has been cancelled
PR Test NVIDIA ARM / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test NVIDIA / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:31 +08:00

214 lines
7.2 KiB
Python

"""Regression tests for logits processing helpers."""
from __future__ import annotations
import os
import sys
from types import SimpleNamespace
# CI Registration (parsed via AST, runtime no-op)
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from ci_system.ci_register import register_cuda_ci # noqa: E402
register_cuda_ci(est_time=90, suite="runtime-1gpu")
import pytest # noqa: E402
import torch # noqa: E402
import tokenspeed.runtime.layers.logits_processor as logits_processor_module # noqa: E402
from tokenspeed.runtime.execution.forward_batch_info import ForwardMode # noqa: E402
from tokenspeed.runtime.layers.logits_processor import ( # noqa: E402
LogitsMetadata,
LogitsProcessor,
fused_softcap,
)
def test_logits_processor_only_uses_fused_lm_head_for_kimi(monkeypatch):
hidden_states = torch.tensor([[1.0, 2.0]], dtype=torch.float32)
lm_head = SimpleNamespace(weight=torch.eye(2, dtype=torch.float32))
metadata = LogitsMetadata(forward_mode=ForwardMode.DECODE)
calls = {"fused": 0}
def fake_lm_head_matmul(hidden, weight):
calls["fused"] += 1
return torch.matmul(hidden.to(weight.dtype), weight.T)
monkeypatch.setattr(logits_processor_module, "_lm_head_matmul", fake_lm_head_matmul)
non_kimi = LogitsProcessor(config=SimpleNamespace(model_type="test", vocab_size=2))
non_kimi(
input_ids=None,
hidden_states=hidden_states,
lm_head=lm_head,
logits_metadata=metadata,
)
assert calls["fused"] == 0
kimi = LogitsProcessor(config=SimpleNamespace(model_type="kimi_k2", vocab_size=2))
kimi(
input_ids=None,
hidden_states=hidden_states,
lm_head=lm_head,
logits_metadata=metadata,
)
assert calls["fused"] == 1
def test_tp_logits_all_gather_handles_zero_rows(monkeypatch):
processor = LogitsProcessor(
config=SimpleNamespace(model_type="test", vocab_size=6),
tp_rank=0,
tp_size=2,
tp_group=(0, 1),
)
hidden_states = torch.empty((0, 2), dtype=torch.float32)
lm_head = SimpleNamespace(weight=torch.ones((3, 2), dtype=torch.float32))
metadata = LogitsMetadata(forward_mode=ForwardMode.DECODE)
calls = {"all_gather": 0}
def fake_all_gather_into_tensor(output, input_, group):
calls["all_gather"] += 1
assert group == (0, 1)
assert tuple(output.shape) == (0, 3)
assert tuple(input_.shape) == (0, 3)
monkeypatch.setattr(
logits_processor_module,
"all_gather_into_tensor",
fake_all_gather_into_tensor,
)
output = processor(
input_ids=None,
hidden_states=hidden_states,
lm_head=lm_head,
logits_metadata=metadata,
)
assert calls["all_gather"] == 1
assert tuple(output.next_token_logits.shape) == (0, 6)
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA required")
def test_fused_softcap_handles_large_logits_without_nan():
cap = 30.0
logits = torch.tensor(
[[5000.0, 2000.0, 1500.0, 100.0, 0.0, -100.0, -1500.0, -5000.0]],
device="cuda",
dtype=torch.float32,
)
expected = cap * torch.tanh(logits / cap)
out = fused_softcap(logits.clone(), cap)
torch.cuda.synchronize()
assert torch.isfinite(out).all()
torch.testing.assert_close(out, expected, rtol=1e-5, atol=2e-5)
def test_argmax_routes_sharded_to_kernel(monkeypatch):
"""Sharded logits (tp shards reconstruct vocab) hit the fused kernel."""
proc = LogitsProcessor(
config=SimpleNamespace(model_type="test", vocab_size=8),
tp_rank=0,
tp_size=2,
tp_group=(0, 1),
)
proc._dist_argmax_state = object() # non-None, non-sentinel => active
recorded = {}
def fake_dist(state, logits):
recorded["called"] = True
return None, logits.argmax(dim=-1)
monkeypatch.setattr(logits_processor_module, "distributed_argmax", fake_dist)
shard = torch.randn(4, 4, dtype=torch.float32) # 4 * tp_size(2) == vocab_size(8)
ids = proc._argmax(shard)
assert recorded.get("called")
assert torch.equal(ids, shard.argmax(dim=-1))
def test_argmax_falls_back_without_state(monkeypatch):
"""No fused state (e.g. EAGLE3 draft vocab != target, or gate failed):
_argmax falls back to a plain argmax instead of routing to the kernel."""
proc = LogitsProcessor(
config=SimpleNamespace(model_type="test", vocab_size=100),
tp_rank=0,
tp_size=2,
tp_group=(0, 1),
)
proc._dist_argmax_state = None # gate failed (draft vocab != target vocab)
monkeypatch.setattr(
logits_processor_module,
"distributed_argmax",
lambda *a, **k: pytest.fail("kernel must not run without a fused state"),
)
# Gathered draft logits are narrower than the target config.vocab_size.
draft = torch.randn(4, 32, dtype=torch.float32)
ids = proc._argmax(draft)
assert torch.equal(ids, draft.argmax(dim=-1))
def test_get_logits_skips_gather_when_dist_argmax_active(monkeypatch):
"""do_argmax + active state keeps logits sharded (no all-gather)."""
proc = LogitsProcessor(
config=SimpleNamespace(
model_type="test", vocab_size=8, final_logit_softcapping=None
),
tp_rank=0,
tp_size=2,
tp_group=(0, 1),
do_argmax=True,
)
monkeypatch.setattr(proc, "_init_dist_argmax_state", lambda lm_head: object())
monkeypatch.setattr(
logits_processor_module,
"all_gather_inner",
lambda *a, **k: pytest.fail("gather must be skipped on the fused path"),
)
hidden = torch.randn(4, 2, dtype=torch.float32)
lm_head = SimpleNamespace(weight=torch.randn(4, 2, dtype=torch.float32)) # 4*2 == 8
md = LogitsMetadata(forward_mode=ForwardMode.DECODE)
out = proc._get_logits(hidden, lm_head, md)
assert out.shape == (4, 4) # local shard width retained, not gathered to 8
def test_get_logits_softcap_disables_fused_argmax(monkeypatch):
"""final_logit_softcapping must disable the fused early-return so the
softcap is applied to full-vocab logits (then a plain argmax runs)."""
proc = LogitsProcessor(
config=SimpleNamespace(
model_type="test", vocab_size=8, final_logit_softcapping=30.0
),
tp_rank=0,
tp_size=2,
tp_group=(0, 1),
do_argmax=True,
)
# Fused state is otherwise eligible; softcap must still force the gather.
monkeypatch.setattr(proc, "_init_dist_argmax_state", lambda lm_head: object())
monkeypatch.setattr(proc, "_init_all_gather_state", lambda lm_head: object())
called = {}
def fake_ag(state, logits, **kw):
called["ag"] = True
return logits.repeat(1, proc.tp_size) # [bs, vocab/tp] -> [bs, vocab]
monkeypatch.setattr(logits_processor_module, "all_gather_inner", fake_ag)
monkeypatch.setattr(
logits_processor_module, "fused_softcap_generic", lambda *a, **k: None
)
hidden = torch.randn(4, 2, dtype=torch.float32)
lm_head = SimpleNamespace(weight=torch.randn(4, 2, dtype=torch.float32)) # 4*2 == 8
md = LogitsMetadata(forward_mode=ForwardMode.DECODE)
out = proc._get_logits(hidden, lm_head, md)
assert called.get("ag") # gathered (softcap on full vocab), not early-returned
assert out.shape == (4, 8)