sgl-project--sglang
94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
179 行
7.1 KiB
Python
179 行
7.1 KiB
Python
"""Numerical correctness test for ZAYA1 MoE + MOD under TP>1.
|
|
|
|
Background: the MOD (mixture-of-depths) skip-expert residual blend must be
|
|
combined with the experts output on the correct side of the cross-rank
|
|
all-reduce. ``mod_out = hidden_states * prob`` is replicated on every TP rank,
|
|
so all-reducing it would multiply it by ``tp_size``. The model therefore masks
|
|
the *per-rank partial* experts output before the reduce and only adds the
|
|
replicated ``mod_out`` afterwards:
|
|
|
|
sum_r(mask · partial_r) + (1 - mask) · mod_out
|
|
= mask · experts_out_full + (1 - mask) · mod_out
|
|
|
|
This test drives the *real* helpers used by ``ZayaBlock.forward`` --
|
|
``mod_premask_experts`` and ``mod_blend`` -- so a regression in that math is
|
|
caught. The cross-rank all-reduce is simulated by summing the per-rank partials
|
|
(the masks are replicated, so the sum is exact), which keeps the test runnable
|
|
on CPU CI without a live ``torch.distributed`` group.
|
|
"""
|
|
|
|
import unittest
|
|
|
|
import torch
|
|
|
|
from sglang.srt.models.zaya import mod_blend, mod_premask_experts
|
|
from sglang.test.ci.ci_register import register_cpu_ci
|
|
from sglang.test.test_utils import CustomTestCase
|
|
|
|
register_cpu_ci(est_time=5, suite="base-a-test-cpu")
|
|
|
|
|
|
def _reference_blend(
|
|
hidden_states: torch.Tensor, # [T, H]
|
|
probs: torch.Tensor, # [T, 1]
|
|
indices: torch.Tensor, # [T, 1]
|
|
experts_out_full: torch.Tensor, # [T, H] -- already-reduced full experts output
|
|
num_moe_experts: int,
|
|
) -> torch.Tensor:
|
|
"""Reference: apply the MOD mask to the *full* (already-reduced) experts
|
|
output, then add the skip path. Mirrors the intended algebra directly.
|
|
"""
|
|
mod_mask = (indices != num_moe_experts).to(experts_out_full.dtype)
|
|
mod_out = hidden_states * probs
|
|
return mod_mask * experts_out_full + (1.0 - mod_mask) * mod_out
|
|
|
|
|
|
def _real_tp_blend(
|
|
hidden_states: torch.Tensor,
|
|
probs: torch.Tensor,
|
|
indices: torch.Tensor,
|
|
partial_experts_per_rank: list[torch.Tensor], # one [T, H] per rank
|
|
num_moe_experts: int,
|
|
) -> torch.Tensor:
|
|
"""Production path: ``mod_premask_experts`` per rank -> simulated all-reduce
|
|
(sum) -> ``mod_blend``. Uses the exact helpers ``ZayaBlock.forward`` calls.
|
|
"""
|
|
mod_out = hidden_states * probs
|
|
reduced = None
|
|
mod_mask = None
|
|
for partial in partial_experts_per_rank:
|
|
mask, masked = mod_premask_experts(partial, indices, num_moe_experts)
|
|
mod_mask = mask
|
|
reduced = masked if reduced is None else reduced + masked
|
|
return mod_blend(reduced, mod_mask, mod_out)
|
|
|
|
|
|
def _buggy_old_tp_blend(
|
|
hidden_states: torch.Tensor,
|
|
probs: torch.Tensor,
|
|
indices: torch.Tensor,
|
|
partial_experts_per_rank: list[torch.Tensor],
|
|
num_moe_experts: int,
|
|
) -> torch.Tensor:
|
|
"""Old, broken sequence: all-reduce the replicated ``mod_out`` (so it gets
|
|
scaled by ``tp_size``) then mix. Proves the test catches a regression.
|
|
"""
|
|
tp_size = len(partial_experts_per_rank)
|
|
mod_out_replicated = hidden_states * probs
|
|
mod_out_after_allreduce = mod_out_replicated * tp_size # all-reduce of replicated
|
|
experts_out_full = torch.stack(partial_experts_per_rank, dim=0).sum(dim=0)
|
|
mod_mask = (indices != num_moe_experts).to(experts_out_full.dtype)
|
|
return mod_mask * experts_out_full + (1.0 - mod_mask) * mod_out_after_allreduce
|
|
|
|
|
|
class TestZayaMODUnderTP(CustomTestCase):
|
|
def _make_partials(self, T: int, H: int, tp_size: int):
|
|
torch.manual_seed(31)
|
|
experts_out_full = torch.randn(T, H, dtype=torch.float32) * 0.1
|
|
# Split into ``tp_size`` random partial tensors that sum to the full output.
|
|
partials = []
|
|
remaining = experts_out_full.clone()
|
|
for _ in range(tp_size - 1):
|
|
p = torch.randn_like(remaining) * 0.05
|
|
partials.append(p)
|
|
remaining = remaining - p
|
|
partials.append(remaining)
|
|
return experts_out_full, partials
|
|
|
|
def _make_inputs(self, T: int, H: int, num_experts: int, frac_skip: float):
|
|
torch.manual_seed(7)
|
|
hidden_states = torch.randn(T, H, dtype=torch.float32)
|
|
probs = torch.rand(T, 1, dtype=torch.float32)
|
|
# Build indices: with probability ``frac_skip`` mark token as skip-expert.
|
|
skip_id = num_experts # MOD uses ``num_moe_experts`` as the skip slot
|
|
rand = torch.rand(T, 1)
|
|
real = torch.randint(0, num_experts, (T, 1))
|
|
indices = torch.where(rand < frac_skip, torch.full_like(real, skip_id), real)
|
|
return hidden_states, probs, indices
|
|
|
|
def test_real_helpers_match_reference_for_tp(self):
|
|
"""The real ``mod_premask_experts`` / ``mod_blend`` path must equal the
|
|
reference blend for any TP size and any skip fraction.
|
|
"""
|
|
T, H = 8, 16
|
|
num_experts = 4
|
|
for tp_size in (2, 4, 8):
|
|
for frac_skip in (0.0, 0.5, 1.0):
|
|
hidden_states, probs, indices = self._make_inputs(
|
|
T, H, num_experts, frac_skip
|
|
)
|
|
full, partials = self._make_partials(T, H, tp_size)
|
|
|
|
ref = _reference_blend(hidden_states, probs, indices, full, num_experts)
|
|
real = _real_tp_blend(
|
|
hidden_states, probs, indices, partials, num_experts
|
|
)
|
|
|
|
torch.testing.assert_close(
|
|
real,
|
|
ref,
|
|
atol=1e-5,
|
|
rtol=1e-5,
|
|
msg=f"tp_size={tp_size} frac_skip={frac_skip}",
|
|
)
|
|
|
|
def test_premask_zeroes_skip_tokens(self):
|
|
"""``mod_premask_experts`` must zero the experts contribution exactly on
|
|
skip-routed tokens and pass through real-expert tokens unchanged.
|
|
"""
|
|
T, H = 6, 8
|
|
num_experts = 4
|
|
experts_out = torch.randn(T, H, dtype=torch.float32)
|
|
# Alternate skip / real tokens.
|
|
indices = torch.tensor(
|
|
[[num_experts], [0], [num_experts], [1], [num_experts], [2]],
|
|
dtype=torch.long,
|
|
)
|
|
mod_mask, masked = mod_premask_experts(experts_out, indices, num_experts)
|
|
|
|
skip_rows = indices.squeeze(-1) == num_experts
|
|
self.assertTrue(torch.all(masked[skip_rows] == 0))
|
|
torch.testing.assert_close(masked[~skip_rows], experts_out[~skip_rows])
|
|
# mask is 0 on skip rows, 1 elsewhere.
|
|
self.assertTrue(torch.all(mod_mask.squeeze(-1)[skip_rows] == 0))
|
|
self.assertTrue(torch.all(mod_mask.squeeze(-1)[~skip_rows] == 1))
|
|
|
|
def test_old_blend_is_wrong_when_skip_used(self):
|
|
"""Sanity: confirm the old (all-reduce mod_out) formula diverges from the
|
|
reference so a regression to that behavior would be caught.
|
|
"""
|
|
T, H = 8, 16
|
|
num_experts = 4
|
|
tp_size = 4
|
|
hidden_states, probs, indices = self._make_inputs(
|
|
T, H, num_experts, frac_skip=0.5
|
|
)
|
|
full, partials = self._make_partials(T, H, tp_size)
|
|
|
|
ref = _reference_blend(hidden_states, probs, indices, full, num_experts)
|
|
buggy = _buggy_old_tp_blend(
|
|
hidden_states, probs, indices, partials, num_experts
|
|
)
|
|
|
|
with self.assertRaises(AssertionError):
|
|
torch.testing.assert_close(buggy, ref, atol=1e-3, rtol=1e-3)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|