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

408 lines
15 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""Build synthetic generation warmup requests.
Default server warmup should cover a representative serving path before the
first real request, without copying user traffic. It starts from the model's
sampling defaults, then keeps startup bounded by choosing common low-cost
resolution/frame buckets and trimming the denoising step count.
Image models may run a tiny second step because first/last step paths often
initialize different kernels or scheduler state. Video models cap frames and
steps to keep startup bounded. Explicit warmup resolutions share this builder;
callers send them through the scheduler client so warmup exercises the same
request transport path as real generation.
"""
from copy import copy
from typing import Any
from sglang.multimodal_gen.configs.pipeline_configs.base import ModelTaskType
from sglang.multimodal_gen.configs.sample.sampling_params import SamplingParams
from sglang.multimodal_gen.registry import get_pipeline_config_classes
from sglang.multimodal_gen.runtime.pipelines_core.schedule_batch import Req
from sglang.multimodal_gen.runtime.server_args import (
ServerArgs,
is_ltx2_two_stage_pipeline_name,
)
from sglang.multimodal_gen.runtime.utils.common import parse_size
from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger
logger = init_logger(__name__)
DEFAULT_PLACEHOLDER_PROMPT = "warmup"
DEFAULT_DETAILED_PLACEHOLDER_PROMPT = "A detailed image."
TORCH_COMPILE_REAL_PATH_PREWARM_PROMPTS = (DEFAULT_DETAILED_PLACEHOLDER_PROMPT,)
DEFAULT_LIGHTWEIGHT_IMAGE_RESOLUTION = (64, 64)
SERVER_WARMUP_IMAGE_FALLBACK_RESOLUTION = (512, 512)
SERVER_WARMUP_VIDEO_FALLBACK_RESOLUTION = (832, 480)
SERVER_WARMUP_IMAGE_MAX_AREA = 768 * 768
SERVER_WARMUP_DIFFUSERS_IMAGE_MAX_AREA = 512 * 512
SERVER_WARMUP_VIDEO_MAX_AREA = 832 * 480
SERVER_WARMUP_MAX_VIDEO_FRAMES = 17
SERVER_WARMUP_IMAGE_STEPS = 2
SERVER_WARMUP_VIDEO_STEPS = 2
def get_model_sampling_defaults(server_args: ServerArgs) -> SamplingParams:
pipeline_class_name = server_args.pipeline_class_name
if pipeline_class_name:
config_classes = get_pipeline_config_classes(pipeline_class_name)
if config_classes is not None:
_, sampling_params_cls = config_classes
return sampling_params_cls()
return SamplingParams.from_pretrained(
server_args.model_path,
backend=server_args.backend,
model_id=server_args.model_id,
)
def _resolve_default_warmup_resolution(
server_args: ServerArgs,
sampling_defaults: SamplingParams,
*,
server_based_warmup: bool,
) -> tuple[int, int]:
"""Return the default warmup resolution.
Prefer the model's sampling-default resolution — the most likely real
request shape — so warmup specializes kernels for it. Server-based image
warmup used to shrink this to an area cap (``SERVER_WARMUP_IMAGE_MAX_AREA``,
768x768) to bound startup, but that left a residual first-request
cold-start when the real request is larger (e.g. 1024x1024 paid ~0.1s of
first-shape kernel autotuning, measured on H100).
"""
width = sampling_defaults.width
height = sampling_defaults.height
is_image_gen = server_args.pipeline_config.task_type.is_image_gen()
if (
width is not None
and height is not None
and (not server_based_warmup or is_image_gen)
):
return width, height
if server_based_warmup:
return _resolve_representative_warmup_resolution(server_args, sampling_defaults)
supported_resolutions = sampling_defaults.supported_resolutions
if supported_resolutions:
return min(supported_resolutions, key=lambda size: size[0] * size[1])
if server_args.pipeline_config.task_type.is_image_gen():
return DEFAULT_LIGHTWEIGHT_IMAGE_RESOLUTION
return (
width or DEFAULT_LIGHTWEIGHT_IMAGE_RESOLUTION[0],
height or DEFAULT_LIGHTWEIGHT_IMAGE_RESOLUTION[1],
)
def _resolve_representative_warmup_resolution(
server_args: ServerArgs,
sampling_defaults: SamplingParams,
) -> tuple[int, int]:
target_area = _target_warmup_area(server_args)
alignment = _warmup_resolution_alignment(server_args)
supported_resolution = _select_supported_warmup_resolution(
sampling_defaults.supported_resolutions, target_area, alignment
)
if supported_resolution is not None:
return supported_resolution
width = sampling_defaults.width
height = sampling_defaults.height
if width is not None and height is not None:
return _fit_resolution_to_area(width, height, target_area, alignment)
width, height = _fallback_warmup_resolution(server_args)
return _fit_resolution_to_area(width, height, target_area, alignment)
def _target_warmup_area(server_args: ServerArgs) -> int:
if server_args.pipeline_config.task_type.is_image_gen():
if getattr(server_args, "backend", None) == "diffusers":
return SERVER_WARMUP_DIFFUSERS_IMAGE_MAX_AREA
return SERVER_WARMUP_IMAGE_MAX_AREA
if _is_video_warmup_task(server_args):
return SERVER_WARMUP_VIDEO_MAX_AREA
return (
DEFAULT_LIGHTWEIGHT_IMAGE_RESOLUTION[0]
* DEFAULT_LIGHTWEIGHT_IMAGE_RESOLUTION[1]
)
def _fallback_warmup_resolution(server_args: ServerArgs) -> tuple[int, int]:
if server_args.pipeline_config.task_type.is_image_gen():
return SERVER_WARMUP_IMAGE_FALLBACK_RESOLUTION
if _is_video_warmup_task(server_args):
return SERVER_WARMUP_VIDEO_FALLBACK_RESOLUTION
return DEFAULT_LIGHTWEIGHT_IMAGE_RESOLUTION
def _is_video_warmup_task(server_args: ServerArgs) -> bool:
return server_args.pipeline_config.task_type.is_video_gen()
def _warmup_resolution_alignment(server_args: ServerArgs) -> int:
pipeline_config = server_args.pipeline_config
alignment = 16
vae_stride = getattr(pipeline_config, "vae_stride", None)
if vae_stride is not None:
spatial_stride = (
vae_stride[-2:] if isinstance(vae_stride, (tuple, list)) else (vae_stride,)
)
for stride in spatial_stride:
alignment = max(alignment, int(stride))
vae_scale_factor = getattr(pipeline_config, "vae_scale_factor", None)
if vae_scale_factor is not None:
alignment = max(alignment, int(vae_scale_factor))
arch_config = getattr(
getattr(pipeline_config, "vae_config", None), "arch_config", None
)
for attr in ("vae_scale_factor", "spatial_compression_ratio"):
value = getattr(arch_config, attr, None)
if value is not None:
alignment = max(alignment, int(value))
if is_ltx2_two_stage_pipeline_name(server_args.pipeline_class_name):
vae_scale_factor = pipeline_config.vae_scale_factor
alignment = max(alignment, 64, int(vae_scale_factor) * 2)
return alignment
def _select_supported_warmup_resolution(
supported_resolutions: list[tuple[int, int]] | None,
target_area: int,
alignment: int,
) -> tuple[int, int] | None:
if not supported_resolutions:
return None
candidates = [
resolution
for resolution in supported_resolutions
if resolution[0] * resolution[1] <= target_area
and _is_resolution_aligned(resolution, alignment)
]
if candidates:
return max(candidates, key=lambda size: size[0] * size[1])
aligned_resolutions = [
resolution
for resolution in supported_resolutions
if _is_resolution_aligned(resolution, alignment)
]
if aligned_resolutions:
return min(aligned_resolutions, key=lambda size: size[0] * size[1])
return None
def _fit_resolution_to_area(
width: int, height: int, target_area: int, alignment: int
) -> tuple[int, int]:
"""adjust the warmup resolution to balance between warmup time and warmup effect"""
area = width * height
if area > target_area:
scale = (target_area / area) ** 0.5
width = int(width * scale)
height = int(height * scale)
return (
max(alignment, width // alignment * alignment),
max(alignment, height // alignment * alignment),
)
def _is_resolution_aligned(resolution: tuple[int, int], alignment: int) -> bool:
width, height = resolution
return width % alignment == 0 and height % alignment == 0
def _resolve_warmup_num_frames(
server_args: ServerArgs,
sampling_defaults: SamplingParams,
*,
server_based_warmup: bool,
) -> int:
num_frames = getattr(sampling_defaults, "num_frames", 1)
if (
not server_based_warmup
or not _is_video_warmup_task(server_args)
or num_frames is None
):
# use default num frames
return num_frames
return min(num_frames, SERVER_WARMUP_MAX_VIDEO_FRAMES)
def _effective_cfg_scale(sampling_defaults: SamplingParams) -> float | None:
if getattr(sampling_defaults, "true_cfg_scale", None) is not None:
return sampling_defaults.true_cfg_scale
return getattr(sampling_defaults, "guidance_scale", None)
def _resolve_warmup_steps(
server_args: ServerArgs,
sampling_defaults: SamplingParams,
*,
server_based_warmup: bool,
) -> int:
warmup_steps = server_args.warmup_steps
default_steps = sampling_defaults.num_inference_steps
# Breakable CUDA graph captures one graph per step-branch at warmup so that
# serving never records a fresh graph. Run the model's full recommended
# steps (uncapped) so every step-branch signature is captured up front.
if (
getattr(server_args, "enable_breakable_cuda_graph", False) is True
and default_steps
):
return max(int(default_steps), warmup_steps)
if not server_based_warmup:
return warmup_steps
if server_args.enable_torch_compile and server_args.is_arg_explicitly_set(
"warmup_steps"
):
return warmup_steps
default_steps = sampling_defaults.num_inference_steps
if default_steps is None or default_steps <= warmup_steps:
return warmup_steps
if _is_video_warmup_task(server_args):
return min(default_steps, max(warmup_steps, SERVER_WARMUP_VIDEO_STEPS))
if server_args.pipeline_config.task_type.is_image_gen():
return min(default_steps, max(warmup_steps, SERVER_WARMUP_IMAGE_STEPS))
return warmup_steps
def should_include_warmup_image(
server_args: ServerArgs, server_based_warmup: bool
) -> bool:
task_type = server_args.pipeline_config.task_type
if not supports_synthetic_warmup(server_args):
return False
if not task_type.accepts_image_input():
return False
if task_type.requires_image_input():
return True
if type(server_args.pipeline_config).__name__ == "GlmImagePipelineConfig":
return False
if server_based_warmup:
return task_type in (ModelTaskType.TI2I, ModelTaskType.TI2V)
return True
def supports_synthetic_warmup(server_args: ServerArgs) -> bool:
task_type = server_args.pipeline_config.task_type
return task_type.is_visual_gen() or task_type.is_mesh_gen()
def build_warmup_reqs(
server_args: ServerArgs,
*,
warmup_resolutions: list[str] | None,
warmup_input_path: str | None = None,
return_warmup_result: bool = False,
server_based_warmup: bool = False,
) -> list[Req]:
task_type = server_args.pipeline_config.task_type
if not supports_synthetic_warmup(server_args):
return []
sampling_defaults = get_model_sampling_defaults(server_args)
if warmup_resolutions is None:
width, height = _resolve_default_warmup_resolution(
server_args,
sampling_defaults,
server_based_warmup=server_based_warmup,
)
resolutions: list[tuple[int, int]] = [(width, height)]
else:
resolutions = [parse_size(resolution) for resolution in warmup_resolutions]
negative_prompt: Any = getattr(sampling_defaults, "negative_prompt", None)
cfg_scale = _effective_cfg_scale(sampling_defaults)
warmup_steps = _resolve_warmup_steps(
server_args,
sampling_defaults,
server_based_warmup=server_based_warmup,
)
warmup_num_frames = _resolve_warmup_num_frames(
server_args,
sampling_defaults,
server_based_warmup=server_based_warmup,
)
# build warmup reqs
warmup_reqs = []
include_warmup_image = should_include_warmup_image(server_args, server_based_warmup)
for width, height in resolutions:
req_kwargs = dict(
data_type=task_type.data_type(),
width=width,
height=height,
prompt=DEFAULT_PLACEHOLDER_PROMPT,
)
req_kwargs["sampling_params"] = copy(sampling_defaults)
req_kwargs.update(
negative_prompt=negative_prompt,
guidance_scale=sampling_defaults.guidance_scale,
guidance_scale_2=sampling_defaults.guidance_scale_2,
true_cfg_scale=sampling_defaults.true_cfg_scale,
num_inference_steps=sampling_defaults.num_inference_steps,
num_frames=warmup_num_frames,
)
if include_warmup_image:
if warmup_input_path is None:
raise RuntimeError(
"Warmup image path is required for image-input model"
)
req_kwargs["prompt"] = DEFAULT_PLACEHOLDER_PROMPT
req_kwargs["image_path"] = [warmup_input_path]
if server_args.enable_cfg_parallel:
if not req_kwargs.get("negative_prompt"):
req_kwargs["negative_prompt"] = DEFAULT_PLACEHOLDER_PROMPT
req_kwargs["do_classifier_free_guidance"] = True
elif negative_prompt is not None and cfg_scale is not None and cfg_scale > 1.0:
req_kwargs["do_classifier_free_guidance"] = True
run_real_path_prewarm = server_based_warmup and server_args.enable_torch_compile
prompts = (
(DEFAULT_PLACEHOLDER_PROMPT,) + TORCH_COMPILE_REAL_PATH_PREWARM_PROMPTS
if run_real_path_prewarm
else (DEFAULT_PLACEHOLDER_PROMPT,)
)
for prompt_idx, prompt in enumerate(prompts):
prompt_req_kwargs = req_kwargs.copy()
prompt_req_kwargs["prompt"] = prompt
prompt_req_kwargs["sampling_params"] = copy(req_kwargs["sampling_params"])
req = Req(**prompt_req_kwargs)
if not run_real_path_prewarm or prompt_idx == 0:
req.set_as_warmup(warmup_steps)
else:
req.sampling_params.num_inference_steps = warmup_steps
req.save_output = False
req.suppress_logs = True
req.metrics.suppress_stage_breakdown = True
req.extra["server_internal_prewarm"] = True
if return_warmup_result:
req.extra["return_warmup_result"] = True
if server_based_warmup:
req.extra["server_based_warmup"] = True
warmup_reqs.append(req)
return warmup_reqs