Tiny adjust CI run suite (#15074)
This commit is contained in:
@@ -3,9 +3,10 @@ import subprocess
|
|||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Callable, List, Optional
|
from typing import Callable, List, Optional, Union
|
||||||
|
|
||||||
from sglang.srt.utils.common import kill_process_tree
|
from sglang.srt.utils.common import kill_process_tree
|
||||||
|
from sglang.test.ci.ci_register import CIRegistry
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -39,7 +40,9 @@ def run_with_timeout(
|
|||||||
|
|
||||||
|
|
||||||
def run_unittest_files(
|
def run_unittest_files(
|
||||||
files: List[TestFile], timeout_per_file: float, continue_on_error: bool = False
|
files: Union[List[TestFile], List[CIRegistry]],
|
||||||
|
timeout_per_file: float,
|
||||||
|
continue_on_error: bool = False,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Run a list of test files.
|
Run a list of test files.
|
||||||
@@ -56,7 +59,12 @@ def run_unittest_files(
|
|||||||
failed_tests = []
|
failed_tests = []
|
||||||
|
|
||||||
for i, file in enumerate(files):
|
for i, file in enumerate(files):
|
||||||
|
if isinstance(file, CIRegistry):
|
||||||
|
filename, estimated_time = file.filename, file.est_time
|
||||||
|
else:
|
||||||
|
# FIXME: remove this branch after migrating all tests to use CIRegistry
|
||||||
filename, estimated_time = file.name, file.estimated_time
|
filename, estimated_time = file.name, file.estimated_time
|
||||||
|
|
||||||
process = None
|
process = None
|
||||||
|
|
||||||
def run_one_file(filename):
|
def run_one_file(filename):
|
||||||
|
|||||||
+54
-29
@@ -3,8 +3,10 @@ import glob
|
|||||||
import sys
|
import sys
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
import tabulate
|
||||||
|
|
||||||
from sglang.test.ci.ci_register import CIRegistry, HWBackend, collect_tests
|
from sglang.test.ci.ci_register import CIRegistry, HWBackend, collect_tests
|
||||||
from sglang.test.ci.ci_utils import TestFile, run_unittest_files
|
from sglang.test.ci.ci_utils import run_unittest_files
|
||||||
|
|
||||||
HW_MAPPING = {
|
HW_MAPPING = {
|
||||||
"cpu": HWBackend.CPU,
|
"cpu": HWBackend.CPU,
|
||||||
@@ -53,7 +55,6 @@ def filter_tests(
|
|||||||
if t.backend == hw and t.suite == suite and t.nightly == nightly
|
if t.backend == hw and t.suite == suite and t.nightly == nightly
|
||||||
]
|
]
|
||||||
|
|
||||||
ret = []
|
|
||||||
valid_suites = (
|
valid_suites = (
|
||||||
NIGHTLY_SUITES.get(hw, []) if nightly else PER_COMMIT_SUITES.get(hw, [])
|
NIGHTLY_SUITES.get(hw, []) if nightly else PER_COMMIT_SUITES.get(hw, [])
|
||||||
)
|
)
|
||||||
@@ -63,17 +64,13 @@ def filter_tests(
|
|||||||
f"Warning: Unknown suite {suite} for backend {hw.name}, nightly={nightly}"
|
f"Warning: Unknown suite {suite} for backend {hw.name}, nightly={nightly}"
|
||||||
)
|
)
|
||||||
|
|
||||||
for t in ci_tests:
|
enabled_tests = [t for t in ci_tests if t.disabled is None]
|
||||||
if t.disabled is None:
|
skipped_tests = [t for t in ci_tests if t.disabled is not None]
|
||||||
ret.append(t)
|
|
||||||
print(f"Including test {t.filename}")
|
|
||||||
else:
|
|
||||||
print(f"Skipping disabled test {t.filename} due to: {t.disabled}")
|
|
||||||
|
|
||||||
return ret
|
return enabled_tests, skipped_tests
|
||||||
|
|
||||||
|
|
||||||
def auto_partition(files, rank, size):
|
def auto_partition(files: List[CIRegistry], rank, size):
|
||||||
"""
|
"""
|
||||||
Partition files into size sublists with approximately equal sums of estimated times
|
Partition files into size sublists with approximately equal sums of estimated times
|
||||||
using a greedy algorithm (LPT heuristic), and return the partition for the specified rank.
|
using a greedy algorithm (LPT heuristic), and return the partition for the specified rank.
|
||||||
@@ -82,7 +79,7 @@ def auto_partition(files, rank, size):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
# Sort files by estimated_time in descending order (LPT heuristic)
|
# Sort files by estimated_time in descending order (LPT heuristic)
|
||||||
sorted_files = sorted(files, key=lambda f: f.estimated_time, reverse=True)
|
sorted_files = sorted(files, key=lambda f: f.est_time, reverse=True)
|
||||||
|
|
||||||
partitions = [[] for _ in range(size)]
|
partitions = [[] for _ in range(size)]
|
||||||
partition_sums = [0.0] * size
|
partition_sums = [0.0] * size
|
||||||
@@ -91,13 +88,51 @@ def auto_partition(files, rank, size):
|
|||||||
for file in sorted_files:
|
for file in sorted_files:
|
||||||
min_sum_idx = min(range(size), key=partition_sums.__getitem__)
|
min_sum_idx = min(range(size), key=partition_sums.__getitem__)
|
||||||
partitions[min_sum_idx].append(file)
|
partitions[min_sum_idx].append(file)
|
||||||
partition_sums[min_sum_idx] += file.estimated_time
|
partition_sums[min_sum_idx] += file.est_time
|
||||||
|
|
||||||
if rank < size:
|
if rank < size:
|
||||||
return partitions[rank]
|
return partitions[rank]
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def pretty_print_tests(
|
||||||
|
args, ci_tests: List[CIRegistry], skipped_tests: List[CIRegistry]
|
||||||
|
):
|
||||||
|
hw = HW_MAPPING[args.hw]
|
||||||
|
suite = args.suite
|
||||||
|
nightly = args.nightly
|
||||||
|
if args.auto_partition_size:
|
||||||
|
partition_info = (
|
||||||
|
f"{args.auto_partition_id + 1}/{args.auto_partition_size} "
|
||||||
|
f"(0-based id={args.auto_partition_id})"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
partition_info = "full"
|
||||||
|
|
||||||
|
headers = ["Hardware", "Suite", "Nightly", "Partition"]
|
||||||
|
rows = [[hw.name, suite, str(nightly), partition_info]]
|
||||||
|
msg = tabulate.tabulate(rows, headers=headers, tablefmt="psql")
|
||||||
|
print(msg + "\n")
|
||||||
|
|
||||||
|
if skipped_tests:
|
||||||
|
print(f"⚠️ Skipped {len(skipped_tests)} test(s):")
|
||||||
|
for t in skipped_tests:
|
||||||
|
reason = t.disabled or "disabled"
|
||||||
|
print(f" - {t.filename} (reason: {reason})")
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
if len(ci_tests) == 0:
|
||||||
|
print(f"No tests found for hw={hw.name}, suite={suite}, nightly={nightly}")
|
||||||
|
print("This is expected during incremental migration. Skipping.")
|
||||||
|
return
|
||||||
|
|
||||||
|
total_est_time = sum(t.est_time for t in ci_tests)
|
||||||
|
print(f"✅ Enabled {len(ci_tests)} test(s) (est total {total_est_time:.1f}s):")
|
||||||
|
for t in ci_tests:
|
||||||
|
print(f" - {t.filename} (est_time={t.est_time})")
|
||||||
|
|
||||||
|
|
||||||
def run_a_suite(args):
|
def run_a_suite(args):
|
||||||
hw = HW_MAPPING[args.hw]
|
hw = HW_MAPPING[args.hw]
|
||||||
suite = args.suite
|
suite = args.suite
|
||||||
@@ -111,29 +146,19 @@ def run_a_suite(args):
|
|||||||
sanity_check = False # Allow files without registration during migration
|
sanity_check = False # Allow files without registration during migration
|
||||||
else:
|
else:
|
||||||
files = glob.glob("registered/**/*.py", recursive=True)
|
files = glob.glob("registered/**/*.py", recursive=True)
|
||||||
sanity_check = (
|
# Strict: all registered files must have proper registration
|
||||||
True # Strict: all registered files must have proper registration
|
sanity_check = True
|
||||||
)
|
|
||||||
|
|
||||||
ci_tests = filter_tests(
|
all_tests = collect_tests(files, sanity_check=sanity_check)
|
||||||
collect_tests(files, sanity_check=sanity_check), hw, suite, nightly
|
ci_tests, skipped_tests = filter_tests(all_tests, hw, suite, nightly)
|
||||||
)
|
|
||||||
test_files = [TestFile(t.filename, t.est_time) for t in ci_tests]
|
|
||||||
|
|
||||||
if not test_files:
|
|
||||||
print(f"No tests found for hw={hw.name}, suite={suite}, nightly={nightly}")
|
|
||||||
print("This is expected during incremental migration. Skipping.")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if auto_partition_size:
|
if auto_partition_size:
|
||||||
test_files = auto_partition(test_files, auto_partition_id, auto_partition_size)
|
ci_tests = auto_partition(ci_tests, auto_partition_id, auto_partition_size)
|
||||||
|
|
||||||
print(
|
pretty_print_tests(args, ci_tests, skipped_tests)
|
||||||
f"Running {len(test_files)} test(s) for hw={hw.name}, suite={suite}, nightly={nightly}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return run_unittest_files(
|
return run_unittest_files(
|
||||||
test_files,
|
ci_tests,
|
||||||
timeout_per_file=args.timeout_per_file,
|
timeout_per_file=args.timeout_per_file,
|
||||||
continue_on_error=args.continue_on_error,
|
continue_on_error=args.continue_on_error,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user