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

383 lines
12 KiB
Python

# Copied and adapted from: https://github.com/hao-ai-lab/FastVideo
import dataclasses
import json
import logging
import os
import subprocess
import sys
import time
from datetime import datetime
from functools import lru_cache
from pathlib import Path
from typing import Any, Dict, Optional
import torch
from dateutil.tz import UTC
import sglang
import sglang.multimodal_gen.envs as envs
from sglang.multimodal_gen.runtime.platforms import current_platform
from sglang.multimodal_gen.runtime.utils.logging_utils import (
CYAN,
RESET,
_SGLDiffusionLogger,
get_is_main_process,
init_logger,
)
logger = init_logger(__name__)
@dataclasses.dataclass
class MemorySnapshot:
allocated_mb: float # current allocated memory
reserved_mb: float # current reserved memory (actual VRAM)
peak_allocated_mb: float # peak allocated since last reset
peak_reserved_mb: float # peak reserved since last reset
def to_dict(self) -> Dict[str, Any]:
return {
"allocated_mb": round(self.allocated_mb, 2),
"reserved_mb": round(self.reserved_mb, 2),
"peak_allocated_mb": round(self.peak_allocated_mb, 2),
"peak_reserved_mb": round(self.peak_reserved_mb, 2),
}
@dataclasses.dataclass
class RequestMetrics:
"""Performance metrics for a single request, including timings and memory snapshots."""
def __init__(self, request_id: str):
self.request_id = request_id
self.stages: Dict[str, float] = {}
self.steps: list[float] = []
self.total_duration_ms: float = 0.0
self.suppress_stage_breakdown: bool = False
# memory tracking: {checkpoint_name: MemorySnapshot}
self.memory_snapshots: Dict[str, MemorySnapshot] = {}
@property
def total_duration_s(self) -> float:
return self.total_duration_ms / 1000.0
def record_stage(self, stage_name: str, duration_s: float):
"""Records the duration of a pipeline stage"""
if self.suppress_stage_breakdown:
return
self.stages[stage_name] = duration_s * 1000 # Store as milliseconds
def record_step(self, duration_s: float):
"""Records the duration of a denoising step in execution order."""
if self.suppress_stage_breakdown:
return
self.steps.append(duration_s * 1000)
def record_memory_snapshot(self, checkpoint_name: str, snapshot: MemorySnapshot):
if self.suppress_stage_breakdown:
return
self.memory_snapshots[checkpoint_name] = snapshot
def to_dict(self) -> Dict[str, Any]:
"""Serializes the metrics data to a dictionary."""
return {
"request_id": self.request_id,
"stages": self.stages,
"steps": self.steps,
"total_duration_ms": self.total_duration_ms,
"memory_snapshots": {
name: snapshot.to_dict()
for name, snapshot in self.memory_snapshots.items()
},
}
def get_diffusion_perf_log_dir() -> str:
"""
Determines the directory for performance logs.
"""
log_dir = os.environ.get("SGLANG_PERF_LOG_DIR")
if log_dir:
return os.path.abspath(log_dir)
if log_dir is None:
sglang_path = Path(sglang.__file__).resolve()
target_path = (sglang_path.parent / "../../.cache/logs").resolve()
return str(target_path)
return ""
@lru_cache(maxsize=1)
def get_git_commit_hash() -> str:
try:
commit_hash = os.environ.get("SGLANG_GIT_COMMIT")
if not commit_hash:
commit_hash = (
subprocess.check_output(
["git", "rev-parse", "HEAD"], stderr=subprocess.DEVNULL
)
.strip()
.decode("utf-8")
)
_CACHED_COMMIT_HASH = commit_hash
return commit_hash
except (subprocess.CalledProcessError, FileNotFoundError):
_CACHED_COMMIT_HASH = "N/A"
return "N/A"
def capture_memory_snapshot() -> MemorySnapshot:
if not torch.get_device_module().is_available():
return MemorySnapshot(
allocated_mb=0.0,
reserved_mb=0.0,
peak_allocated_mb=0.0,
peak_reserved_mb=0.0,
)
allocated = torch.get_device_module().memory_allocated()
reserved = torch.get_device_module().memory_reserved()
peak_allocated = torch.get_device_module().max_memory_allocated()
peak_reserved = torch.get_device_module().max_memory_reserved()
return MemorySnapshot(
allocated_mb=allocated / (1024**2),
reserved_mb=reserved / (1024**2),
peak_allocated_mb=peak_allocated / (1024**2),
peak_reserved_mb=peak_reserved / (1024**2),
)
@dataclasses.dataclass
class RequestPerfRecord:
request_id: str
timestamp: str
commit_hash: str
tag: str
stages: list[dict]
steps: list[float]
total_duration_ms: float
memory_snapshots: dict[str, dict] = dataclasses.field(default_factory=dict)
def __init__(
self,
request_id,
commit_hash,
tag,
stages,
steps,
total_duration_ms,
memory_snapshots=None,
timestamp=None,
):
self.request_id = request_id
if timestamp is not None:
self.timestamp = timestamp
else:
self.timestamp = datetime.now(UTC).isoformat()
self.commit_hash = commit_hash
self.tag = tag
self.stages = stages
self.steps = steps
self.total_duration_ms = total_duration_ms
self.memory_snapshots = memory_snapshots or {}
class StageProfiler:
"""
A unified context manager, records performance metrics (usually of a single Stage or a step) into a provided RequestMetrics object (usually from a Req).
"""
def __init__(
self,
stage_name: str,
logger: _SGLDiffusionLogger,
metrics: Optional["RequestMetrics"],
log_stage_start_end: bool = False,
perf_dump_path_provided: bool = False,
capture_memory: bool = False,
record_as_step: bool = False,
):
self.stage_name = stage_name
self.metrics = metrics
self.logger = logger
self.start_time = 0.0
self.log_timing = perf_dump_path_provided or envs.SGLANG_DIFFUSION_STAGE_LOGGING
self.log_stage_start_end = log_stage_start_end
self.capture_memory = capture_memory
self.record_as_step = record_as_step
def _should_record_as_step(self) -> bool:
return self.record_as_step or self.stage_name.startswith("denoising_step_")
def __enter__(self):
if self.log_stage_start_end:
msg = f"[{self.stage_name}] started..."
if self.logger.isEnabledFor(logging.DEBUG):
# This debug-only memory log runs at every stage boundary in CI.
# Keep it observational; cache cleanup is handled at explicit
# failure and component-release points.
available_memory = current_platform.get_available_gpu_memory(
empty_cache=False
)
msg += f" ({round(available_memory, 2)} GB left)"
self.logger.info(msg)
if (self.log_timing and self.metrics) or self.log_stage_start_end:
if (
os.environ.get("SGLANG_DIFFUSION_SYNC_STAGE_PROFILING", "0") == "1"
and self._should_record_as_step()
and torch.get_device_module().is_available()
):
torch.get_device_module().synchronize()
self.start_time = time.perf_counter()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if not ((self.log_timing and self.metrics) or self.log_stage_start_end):
return False
if (
os.environ.get("SGLANG_DIFFUSION_SYNC_STAGE_PROFILING", "0") == "1"
and self._should_record_as_step()
and torch.get_device_module().is_available()
):
torch.get_device_module().synchronize()
execution_time_s = time.perf_counter() - self.start_time
if exc_type:
self.logger.error(
"[%s] Error during execution after %.4f ms: %s",
self.stage_name,
execution_time_s * 1000,
exc_val,
exc_info=True,
)
return False
if self.log_stage_start_end:
self.logger.info(
f"[{self.stage_name}] finished in {execution_time_s:.4f} seconds",
)
if self.log_timing and self.metrics:
if self._should_record_as_step():
self.metrics.record_step(execution_time_s)
else:
self.metrics.record_stage(self.stage_name, execution_time_s)
# capture memory snapshot after stage if requested
if self.capture_memory and torch.get_device_module().is_available():
snapshot = capture_memory_snapshot()
self.metrics.record_memory_snapshot(
f"after_{self.stage_name}", snapshot
)
return False
class PerformanceLogger:
"""
A global utility class for logging performance metrics for all request, categorized by request-id.
Serves both as a runtime logger (stream to file) and a dump utility.
Notice that RequestMetrics stores the performance metrics of a single request
"""
@classmethod
def dump_benchmark_report(
cls,
file_path: str,
metrics: "RequestMetrics",
meta: Optional[Dict[str, Any]] = None,
tag: str = "benchmark_dump",
):
"""
Static method to dump a standardized benchmark report to a file.
Eliminates duplicate logic in CLI/Client code.
"""
formatted_steps = [
{"name": name, "duration_ms": duration_ms}
for name, duration_ms in metrics.stages.items()
]
denoise_steps_ms = [
{"step": idx, "duration_ms": duration_ms}
for idx, duration_ms in enumerate(metrics.steps)
]
memory_checkpoints = {
name: snapshot.to_dict()
for name, snapshot in metrics.memory_snapshots.items()
}
report = {
"timestamp": datetime.now(UTC).isoformat(),
"request_id": metrics.request_id,
"commit_hash": get_git_commit_hash(),
"tag": tag,
"total_duration_ms": metrics.total_duration_ms,
"steps": formatted_steps,
"denoise_steps_ms": denoise_steps_ms,
"memory_checkpoints": memory_checkpoints,
"meta": meta or {},
}
try:
abs_path = os.path.abspath(file_path)
os.makedirs(os.path.dirname(abs_path), exist_ok=True)
with open(abs_path, "w", encoding="utf-8") as f:
json.dump(report, f, indent=2)
logger.info(f"Metrics dumped to: {CYAN}{abs_path}{RESET}")
except IOError as e:
logger.error(f"Failed to dump metrics to {abs_path}: {e}")
@classmethod
def log_request_summary(
cls,
metrics: "RequestMetrics",
tag: str = "total_inference_time",
):
"""logs the stage metrics and total duration for a completed request
to the performance_log file.
Note that this accords to the time spent internally in server, postprocess is not included
"""
formatted_stages = [
{"name": name, "execution_time_ms": duration_ms}
for name, duration_ms in metrics.stages.items()
]
memory_checkpoints = {
name: snapshot.to_dict()
for name, snapshot in metrics.memory_snapshots.items()
}
record = RequestPerfRecord(
metrics.request_id,
commit_hash=get_git_commit_hash(),
tag="pipeline_stage_metrics",
stages=formatted_stages,
steps=metrics.steps,
total_duration_ms=metrics.total_duration_ms,
memory_snapshots=memory_checkpoints,
)
try:
if get_is_main_process():
log_dir = get_diffusion_perf_log_dir()
if not os.path.exists(log_dir):
os.makedirs(log_dir, exist_ok=True)
log_file = os.path.join(log_dir, "performance.log")
with open(log_file, "a", encoding="utf-8") as f:
f.write(json.dumps(dataclasses.asdict(record)) + "\n")
except (OSError, PermissionError) as e:
print(f"WARNING: Failed to log performance record: {e}", file=sys.stderr)