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
154 行
6.8 KiB
Python
154 行
6.8 KiB
Python
"""Unit tests for balanced_packing — no server, no model loading."""
|
|
|
|
from sglang.test.ci.ci_register import register_cpu_ci
|
|
|
|
register_cpu_ci(est_time=7, suite="base-a-test-cpu")
|
|
|
|
import unittest
|
|
|
|
import torch
|
|
|
|
from sglang.srt.eplb.eplb_algorithms.deepseek import balanced_packing
|
|
from sglang.test.test_utils import CustomTestCase
|
|
|
|
|
|
class TestBalancedPacking(CustomTestCase):
|
|
"""Tests for balanced_packing(weight, num_packs).
|
|
|
|
Invariants:
|
|
- Output shapes match input: both [X, n].
|
|
- pack_index values are in [0, num_packs).
|
|
- Each pack receives exactly n // num_packs items per layer.
|
|
- rank_in_pack values are in [0, groups_per_pack).
|
|
- Each (pack, rank) slot is used exactly once per layer.
|
|
- Packs are as weight-balanced as possible (greedy optimality).
|
|
"""
|
|
|
|
# ------------------------------------------------------------------ helpers
|
|
|
|
def _check_shapes(self, weight, pack_index, rank_in_pack):
|
|
self.assertEqual(pack_index.shape, weight.shape)
|
|
self.assertEqual(rank_in_pack.shape, weight.shape)
|
|
|
|
def _check_pack_index_range(self, pack_index, num_packs):
|
|
self.assertTrue(torch.all(pack_index >= 0))
|
|
self.assertTrue(torch.all(pack_index < num_packs))
|
|
|
|
def _check_items_per_pack(self, pack_index, num_packs, groups_per_pack):
|
|
"""Every pack must hold exactly groups_per_pack items in every layer."""
|
|
for layer in range(pack_index.shape[0]):
|
|
counts = torch.bincount(pack_index[layer], minlength=num_packs)
|
|
self.assertTrue(
|
|
torch.all(counts == groups_per_pack),
|
|
f"layer {layer}: pack counts {counts.tolist()} != {groups_per_pack}",
|
|
)
|
|
|
|
def _check_rank_in_pack_range(self, rank_in_pack, groups_per_pack):
|
|
self.assertTrue(torch.all(rank_in_pack >= 0))
|
|
self.assertTrue(torch.all(rank_in_pack < groups_per_pack))
|
|
|
|
def _check_unique_slots(self, pack_index, rank_in_pack, num_packs, groups_per_pack):
|
|
"""Each (pack, rank) slot is occupied exactly once per layer."""
|
|
num_layers = pack_index.shape[0]
|
|
for layer in range(num_layers):
|
|
slots = set(zip(pack_index[layer].tolist(), rank_in_pack[layer].tolist()))
|
|
self.assertEqual(len(slots), num_packs * groups_per_pack)
|
|
|
|
# ------------------------------------------------------------------ tests
|
|
|
|
def test_output_shapes(self):
|
|
"""pack_index and rank_in_pack have the same shape as weight."""
|
|
weight = torch.rand(3, 8)
|
|
pack_index, rank_in_pack = balanced_packing(weight, num_packs=4)
|
|
self._check_shapes(weight, pack_index, rank_in_pack)
|
|
|
|
def test_pack_index_range(self):
|
|
"""All pack indices are in [0, num_packs)."""
|
|
weight = torch.rand(2, 6)
|
|
pack_index, _ = balanced_packing(weight, num_packs=3)
|
|
self._check_pack_index_range(pack_index, num_packs=3)
|
|
|
|
def test_each_pack_receives_equal_items(self):
|
|
"""Each pack receives exactly n // num_packs items per layer."""
|
|
weight = torch.rand(4, 8)
|
|
num_packs = 4
|
|
pack_index, _ = balanced_packing(weight, num_packs=num_packs)
|
|
self._check_items_per_pack(pack_index, num_packs, groups_per_pack=2)
|
|
|
|
def test_rank_in_pack_range(self):
|
|
"""rank_in_pack values are in [0, groups_per_pack)."""
|
|
weight = torch.rand(2, 8)
|
|
num_packs = 4
|
|
groups_per_pack = 8 // num_packs
|
|
_, rank_in_pack = balanced_packing(weight, num_packs=num_packs)
|
|
self._check_rank_in_pack_range(rank_in_pack, groups_per_pack)
|
|
|
|
def test_unique_pack_rank_slots(self):
|
|
"""Each (pack, rank) slot is used exactly once per layer."""
|
|
weight = torch.rand(3, 8)
|
|
num_packs = 4
|
|
pack_index, rank_in_pack = balanced_packing(weight, num_packs=num_packs)
|
|
self._check_unique_slots(pack_index, rank_in_pack, num_packs, groups_per_pack=2)
|
|
|
|
def test_groups_per_pack_one_special_case(self):
|
|
"""When groups_per_pack == 1 (num_packs == n), each item gets its own pack."""
|
|
n = 6
|
|
weight = torch.rand(2, n)
|
|
pack_index, rank_in_pack = balanced_packing(weight, num_packs=n)
|
|
# pack_index[layer] should be a permutation of [0, n)
|
|
for layer in range(weight.shape[0]):
|
|
self.assertEqual(sorted(pack_index[layer].tolist()), list(range(n)))
|
|
# rank_in_pack is all zeros
|
|
self.assertTrue(torch.all(rank_in_pack == 0))
|
|
|
|
def test_single_layer(self):
|
|
"""Works correctly with a single layer."""
|
|
weight = torch.tensor([[3.0, 1.0, 4.0, 1.0]])
|
|
pack_index, rank_in_pack = balanced_packing(weight, num_packs=2)
|
|
self._check_shapes(weight, pack_index, rank_in_pack)
|
|
self._check_items_per_pack(pack_index, num_packs=2, groups_per_pack=2)
|
|
|
|
def test_uniform_weights_all_invariants(self):
|
|
"""Uniform weights: all invariants hold regardless of assignment."""
|
|
weight = torch.ones(3, 8)
|
|
num_packs = 4
|
|
pack_index, rank_in_pack = balanced_packing(weight, num_packs=num_packs)
|
|
self._check_shapes(weight, pack_index, rank_in_pack)
|
|
self._check_pack_index_range(pack_index, num_packs)
|
|
self._check_items_per_pack(pack_index, num_packs, groups_per_pack=2)
|
|
self._check_rank_in_pack_range(rank_in_pack, groups_per_pack=2)
|
|
self._check_unique_slots(pack_index, rank_in_pack, num_packs, groups_per_pack=2)
|
|
|
|
def test_balance_property(self):
|
|
"""Heavier items are spread across packs to minimize max pack weight."""
|
|
# Weights: [9, 1, 1, 1] with 2 packs → optimal: {9,1} and {1,1}, not {9,1,1} and {1}
|
|
weight = torch.tensor([[9.0, 1.0, 1.0, 1.0]])
|
|
pack_index, _ = balanced_packing(weight, num_packs=2)
|
|
pack_weights = torch.zeros(2)
|
|
for i, p in enumerate(pack_index[0].tolist()):
|
|
pack_weights[p] += weight[0, i]
|
|
# Max pack weight should be 10 (9+1), not 11 (9+1+1)
|
|
self.assertEqual(pack_weights.max().item(), 10.0)
|
|
|
|
def test_deterministic(self):
|
|
"""Same input always produces the same output."""
|
|
weight = torch.rand(3, 8)
|
|
result1 = balanced_packing(weight.clone(), num_packs=4)
|
|
result2 = balanced_packing(weight.clone(), num_packs=4)
|
|
self.assertTrue(torch.equal(result1[0], result2[0]))
|
|
self.assertTrue(torch.equal(result1[1], result2[1]))
|
|
|
|
def test_many_layers(self):
|
|
"""All invariants hold across many layers."""
|
|
weight = torch.rand(16, 8)
|
|
num_packs = 4
|
|
pack_index, rank_in_pack = balanced_packing(weight, num_packs=num_packs)
|
|
self._check_shapes(weight, pack_index, rank_in_pack)
|
|
self._check_pack_index_range(pack_index, num_packs)
|
|
self._check_items_per_pack(pack_index, num_packs, groups_per_pack=2)
|
|
self._check_unique_slots(pack_index, rank_in_pack, num_packs, groups_per_pack=2)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|