[diffusion] CI: expose nightly server telemetry coverage (#38782)

Co-authored-by: Mick Qian <mickqian@users.noreply.github.com>
This commit is contained in:
Mick
2026-09-11 23:07:37 +08:00
committed by GitHub
co-authored by Mick Qian
parent 7f09fbcd25
commit e016de462c
3 changed files with 61 additions and 4 deletions
+9
View File
@@ -62,3 +62,12 @@ Specify input/output paths and timeout:
```bash ```bash
python python/sglang/multimodal_gen/test/scripts/gen_perf_baselines.py --baseline python/sglang/multimodal_gen/test/server/perf_baselines.json --out /tmp/perf_baselines.json --timeout 600 python python/sglang/multimodal_gen/test/scripts/gen_perf_baselines.py --baseline python/sglang/multimodal_gen/test/server/perf_baselines.json --out /tmp/perf_baselines.json --timeout 600
``` ```
## Nightly Comparison Telemetry
The nightly comparison reports the median of three client-side measurements.
SGLang requests also write server perf dumps for stage-level timing. An
unreadable dump does not invalidate a completed client measurement, but the
dashboard reports server sample coverage (for example, `2/3`) and computes the
server total and stage medians only from readable dumps. Treat incomplete
coverage as a telemetry warning rather than a performance regression by itself.
@@ -117,7 +117,9 @@ def test_dashboard_uses_historical_median_and_shows_server_breakdown():
"latency_s": 10.4, "latency_s": 10.4,
"latency_samples_s": [10.3, 10.4, 10.5], "latency_samples_s": [10.3, 10.4, 10.5],
"measurement_count": 3, "measurement_count": 3,
"server_latency_samples_s": [9.9, 10.0],
"server_latency_s": 10.0, "server_latency_s": 10.0,
"missing_perf_dumps": 1,
"server_stage_medians_ms": { "server_stage_medians_ms": {
"TextEncodingStage": 100.0, "TextEncodingStage": 100.0,
"DenoisingStage": 9800.0, "DenoisingStage": 9800.0,
@@ -148,6 +150,8 @@ def test_dashboard_uses_historical_median_and_shows_server_breakdown():
assert baseline == 10.0 assert baseline == 10.0
assert count == 3 assert count == 3
assert alerts == [] assert alerts == []
assert "| 3 | **10.40** |" in markdown assert "Incomplete Server Telemetry" in markdown
assert "**model**: 2/3 server samples available" in markdown
assert "| 3 | 2/3 | **10.40** |" in markdown
assert "## SGLang Server-Side Breakdown" in markdown assert "## SGLang Server-Side Breakdown" in markdown
assert "| model | 10.00 | 0.10 | 9.80 | 0.10 | 196.00 |" in markdown assert "| model | 10.00 | 0.10 | 9.80 | 0.10 | 196.00 |" in markdown
@@ -258,6 +258,23 @@ def _stage_group_seconds(result: dict, suffixes: str | tuple[str, ...]) -> float
return sum(values) / 1000.0 return sum(values) / 1000.0
def _server_sample_coverage(result: dict) -> tuple[int, int] | None:
total = result.get("measurement_count")
if not isinstance(total, int) or total <= 0:
return None
samples = result.get("server_latency_samples_s")
if isinstance(samples, list):
available = len(samples)
elif "missing_perf_dumps" in result:
available = total - int(result["missing_perf_dumps"])
elif result.get("server_latency_s") is not None:
available = total
else:
available = 0
return max(0, min(available, total)), total
def _sanitize_filename(name: str) -> str: def _sanitize_filename(name: str) -> str:
"""Sanitize a case ID to be a safe filename.""" """Sanitize a case ID to be a safe filename."""
return name.replace("/", "_").replace(" ", "_").replace(":", "_") return name.replace("/", "_").replace(" ", "_").replace(":", "_")
@@ -289,6 +306,16 @@ def generate_dashboard(
current_records = _extract_case_records(current) current_records = _extract_case_records(current)
case_ids = list(current_cases.keys()) case_ids = list(current_cases.keys())
incomplete_server_telemetry = []
for cid in case_ids:
record = current_records.get(cid, {}).get("sglang", {})
coverage = _server_sample_coverage(record)
if coverage is not None and coverage[0] < coverage[1]:
model = record.get("model", cid).split("/")[-1]
incomplete_server_telemetry.append(
f"**{model}**: {coverage[0]}/{coverage[1]}"
)
# ---- Regression detection ---- # ---- Regression detection ----
regressions: list[str] = [] regressions: list[str] = []
if history: if history:
@@ -311,6 +338,16 @@ def generate_dashboard(
lines.append(f"> - {reg}") lines.append(f"> - {reg}")
lines.append("\n") lines.append("\n")
if incomplete_server_telemetry:
lines.append(
"> [!WARNING]\n> **Incomplete Server Telemetry**\n>\n"
"> Client-side latency includes every measured request, but server-side "
"stage medians use only the readable perf dumps."
)
for coverage in incomplete_server_telemetry:
lines.append(f"> - {coverage} server samples available")
lines.append("\n")
# Discover all frameworks present in results # Discover all frameworks present in results
all_frameworks = [] all_frameworks = []
seen_fw = set() seen_fw = set()
@@ -334,8 +371,8 @@ def generate_dashboard(
risk_map[cid] = _assess_risk(cid, current_cases, history, other_frameworks) risk_map[cid] = _assess_risk(cid, current_cases, history, other_frameworks)
# Dynamic header # Dynamic header
header = "| Model | Risk | Samples |" header = "| Model | Risk | Client samples | Server samples |"
sep = "|-------|------|---------|" sep = "|-------|------|----------------|----------------|"
for fw in all_frameworks: for fw in all_frameworks:
header += f" {fw} median (s) |" header += f" {fw} median (s) |"
sep += "---------|" sep += "---------|"
@@ -359,10 +396,17 @@ def generate_dashboard(
sample_count = sg_record.get("measurement_count") sample_count = sg_record.get("measurement_count")
if not sample_count and sg_lat is not None: if not sample_count and sg_lat is not None:
sample_count = 1 sample_count = 1
server_coverage = _server_sample_coverage(sg_record)
server_samples = (
f"{server_coverage[0]}/{server_coverage[1]}"
if server_coverage is not None
else "N/A"
)
risk_emoji, _ = risk_map.get(cid, ("✅", "")) risk_emoji, _ = risk_map.get(cid, ("✅", ""))
row = ( row = (
f"| {r['model'].split('/')[-1]} | {risk_emoji} | {sample_count or 'N/A'} |" f"| {r['model'].split('/')[-1]} | {risk_emoji} | "
f"{sample_count or 'N/A'} | {server_samples} |"
) )
# Latency columns -- bold the fastest # Latency columns -- bold the fastest
lats = {fw: case_fws.get(fw) for fw in all_frameworks} lats = {fw: case_fws.get(fw) for fw in all_frameworks}