Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

216 lines
7.6 KiB
Python

"""Unit tests for the unified SP shard helpers (pure logic, no distributed)."""
import sys
import pytest
import torch
from sglang.multimodal_gen.runtime.distributed import sp_shard_utils as sps
from sglang.multimodal_gen.runtime.distributed.sp_shard_utils import (
SpShard,
shard_like,
tail_attn_meta,
)
def _fake_sp(monkeypatch, sp_size, sp_rank=0, ring=1):
monkeypatch.setattr(sps, "get_sp_world_size", lambda: sp_size)
monkeypatch.setattr(sps, "get_sp_parallel_rank", lambda: sp_rank)
monkeypatch.setattr(sps, "get_ring_parallel_world_size", lambda: ring)
# --- build_shard_plan math --------------------------------------------------------
def test_plan_shard_divisible(monkeypatch):
_fake_sp(monkeypatch, 2, 1)
s = sps.build_shard_plan(16)
assert (s.local_len, s.num_pad, s.local_pad) == (8, 0, 0)
def test_plan_shard_padded_last_rank(monkeypatch):
_fake_sp(monkeypatch, 4, 3)
s = sps.build_shard_plan(14)
assert (s.local_len, s.num_pad) == (4, 2)
assert s.local_pad == 2 and s.local_real_len == 2
def test_plan_shard_pad_only_on_last_rank(monkeypatch):
_fake_sp(monkeypatch, 4, 0)
s = sps.build_shard_plan(14)
assert s.local_pad == 0 and s.local_real_len == 4
def test_plan_shard_sp1_noop(monkeypatch):
_fake_sp(monkeypatch, 1)
s = sps.build_shard_plan(15)
assert (s.local_len, s.num_pad, s.sp_size) == (15, 0, 1)
# --- shard_like -------------------------------------------------------------
def test_shard_like_zero_pads_tail():
shard = SpShard(orig_len=15, local_len=8, num_pad=1, sp_size=2, sp_rank=1)
x = torch.arange(15, dtype=torch.float32).unsqueeze(0).unsqueeze(-1)
local = shard_like(x, shard, dim=1)
assert local.shape[1] == 8
assert local[0, -1, 0].item() == 0.0 # tail pad
assert local[0, 0, 0].item() == 8.0 # rank1 starts at token 8
def test_shard_like_repeat_last():
shard = SpShard(orig_len=15, local_len=8, num_pad=1, sp_size=2, sp_rank=1)
x = torch.arange(15, dtype=torch.float32).unsqueeze(-1)
local = shard_like(x, shard, dim=0, pad_mode="repeat_last")
assert local[-1, 0].item() == 14.0 # repeated last row, not zero
def test_shard_like_chunks_align_across_tensors():
# RoPE cache sharded with the same plan stays aligned with hidden states.
shard = SpShard(orig_len=15, local_len=8, num_pad=1, sp_size=2, sp_rank=0)
x = torch.arange(15).unsqueeze(0).unsqueeze(-1).float()
rope = torch.arange(15).unsqueeze(-1).float()
assert torch.equal(
shard_like(x, shard, dim=1)[0, :, 0], shard_like(rope, shard, dim=0)[:, 0]
)
# --- tail_attn_meta ---------------------------------------------------------
def test_tail_meta_none_when_divisible():
shard = SpShard(orig_len=16, local_len=8, num_pad=0, sp_size=2, sp_rank=0)
assert tail_attn_meta(shard, 1, torch.device("cpu")) is None
def test_tail_meta_single_stream():
shard = SpShard(orig_len=15, local_len=8, num_pad=1, sp_size=2, sp_rank=1)
meta = tail_attn_meta(shard, 1, torch.device("cpu"))
assert meta["pad_start"] == 15 and meta["pad_end"] == 16
assert meta["local_pad"] == 1
assert meta["cu_seqlens_tail"].tolist() == [0, 15, 16]
assert meta["max_seqlen_tail"] == 15
def test_tail_meta_joint_layout_and_batch():
# sp=2, local_txt=8 (1 pad), img=100 per rank -> S = 2*(8+100) = 216.
shard = SpShard(orig_len=15, local_len=8, num_pad=1, sp_size=2, sp_rank=1)
meta = tail_attn_meta(shard, 2, torch.device("cpu"), image_seq_len=100)
assert meta["pad_start"] == 215 and meta["pad_end"] == 216
assert meta["cu_seqlens_tail"].tolist() == [0, 215, 216, 431, 432]
def test_tail_meta_max_seqlen_covers_pad_segment():
# Degenerate short sequence: num_pad (3) > valid (1). FA requires
# max_seqlen >= the longest segment, i.e. the pad block here.
shard = SpShard(orig_len=1, local_len=1, num_pad=3, sp_size=4, sp_rank=3)
meta = tail_attn_meta(shard, 1, torch.device("cpu"))
assert meta["max_seqlen_tail"] == 3
def test_tail_meta_matches_legacy_gap_formula():
# The tail layout puts the pad exactly where the legacy per-model gap
# formula pointed, minus the relocation: end == S (global tail).
sp, local_txt, img, num_pad = 3, 5, 40, 2
shard = SpShard(
orig_len=sp * local_txt - num_pad,
local_len=local_txt,
num_pad=num_pad,
sp_size=sp,
sp_rank=sp - 1,
)
meta = tail_attn_meta(shard, 1, torch.device("cpu"), image_seq_len=img)
seq = sp * (local_txt + img)
assert meta["pad_end"] == seq
assert meta["pad_start"] == seq - num_pad
# --- plan_text_strategy -----------------------------------------------------
def test_strategy_sp1_replicates(monkeypatch):
_fake_sp(monkeypatch, 1)
assert sps.plan_text_strategy(100) == "replicate"
def test_strategy_shard_when_legal(monkeypatch):
_fake_sp(monkeypatch, 2)
assert sps.plan_text_strategy(15) == "shard"
assert sps.plan_text_strategy(16) == "shard"
def test_strategy_ring_blocks_padded_shard(monkeypatch):
_fake_sp(monkeypatch, 2, ring=2)
assert sps.plan_text_strategy(15) == "replicate" # padded shard needs mask
assert sps.plan_text_strategy(16) == "shard" # divisible: no mask needed
def test_strategy_min_len_threshold(monkeypatch):
_fake_sp(monkeypatch, 2)
monkeypatch.setattr(sps, "_TEXT_SHARD_MIN", 64)
assert sps.plan_text_strategy(32) == "replicate"
assert sps.plan_text_strategy(64) == "shard"
# --- join_seqs / split_seqs / shard_seq_prefix ------------------------------
def test_join_split_roundtrip_with_pad():
# Joint [text, image] with 2 tail-pad rows relocated behind the image.
txt = torch.arange(6, dtype=torch.float32).view(1, 6, 1) # rows 4,5 are pad
img = (torch.arange(3, dtype=torch.float32) + 100).view(1, 3, 1)
joint = sps.join_seqs(txt, img, local_pad=2)
assert joint[0, :, 0].tolist() == [0, 1, 2, 3, 100, 101, 102, 4, 5]
txt_back, img_back = sps.split_seqs(joint, prefix_len=6, local_pad=2)
assert torch.equal(txt_back, txt) and torch.equal(img_back, img)
def test_join_split_roundtrip_no_pad():
txt = torch.randn(1, 4, 2)
img = torch.randn(1, 3, 2)
joint = sps.join_seqs(txt, img, local_pad=0)
assert torch.equal(joint, torch.cat([txt, img], dim=1))
txt_back, img_back = sps.split_seqs(joint, prefix_len=4, local_pad=0)
assert torch.equal(txt_back, txt) and torch.equal(img_back, img)
def test_shard_seq_prefix_only_touches_prefix():
# Joint RoPE cache [txt(15); img(4)]: text segment shards, image stays.
shard = SpShard(orig_len=15, local_len=8, num_pad=1, sp_size=2, sp_rank=1)
cache = torch.arange(19, dtype=torch.float32).unsqueeze(-1)
out = sps.shard_seq_prefix(cache, 15, shard, dim=0)
assert out.shape[0] == 8 + 4
assert out[0, 0].item() == 8.0 # rank1 text chunk starts at token 8
assert out[-4:, 0].flatten().tolist() == [15, 16, 17, 18] # image untouched
def test_should_shard_text_gate(monkeypatch):
_fake_sp(monkeypatch, 2)
assert sps.should_shard_text(15) is True
_fake_sp(monkeypatch, 1)
assert sps.should_shard_text(15) is False
# --- gather_seq -------------------------------------------------------------
def test_gather_seq_sp1_noop(monkeypatch):
_fake_sp(monkeypatch, 1)
x = torch.randn(1, 5, 2)
assert sps.gather_seq(x, 5, dim=1) is x
def test_gather_seq_trims(monkeypatch):
_fake_sp(monkeypatch, 2)
monkeypatch.setattr(
sps, "sequence_model_parallel_all_gather", lambda t, dim: torch.cat([t, t], dim)
)
local = torch.randn(1, 8, 2)
out = sps.gather_seq(local, 15, dim=1)
assert out.shape[1] == 15
if __name__ == "__main__":
sys.exit(pytest.main([__file__, "-q"]))