Skip to content

Commit 66cd84e

Browse files
Neil4561Horde
andauthored
Add trustworthy performance bisection
Reconstruct each candidate's pinned stack and measure it with one verified perf-smoke tooling contract. Preserve noise, warmup, blocker, and hardware evidence so first-bad results can be reviewed and reproduced across hosts. Co-authored-by: Horde <horde@neilm-hfez84.cs1cloud.internal>
1 parent 3968457 commit 66cd84e

36 files changed

Lines changed: 10755 additions & 92 deletions

tools/perf_smoke_test/benchmark_result_adapter.py

Lines changed: 39 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,6 @@
3030

3131
from contracts import RuntimeSample
3232

33-
# Map the schema's rendering-backend vocabulary to the gate's render-preset
34-
# tokens used in tasks.json / backend_identity. ``"none"`` (headless, no camera)
35-
# maps to ``None``.
3633
_SCHEMA_RENDER_TO_GATE: dict[str | None, str | None] = {
3734
None: None,
3835
"": None,
@@ -44,52 +41,36 @@
4441

4542

4643
def _as_dict(value: Any) -> dict:
47-
"""Return ``value`` if it is a dict, else an empty dict.
48-
49-
Bundle sections are always dicts in a valid schema-v1 file; this keeps a
50-
malformed/hand-edited bundle (e.g. a list where a dict is expected) from
51-
crashing the projection so the gate degrades gracefully instead of the
52-
result builder aborting with no output.
53-
"""
44+
"""Return ``value`` if it is a dict, else an empty dict."""
5445
return value if isinstance(value, dict) else {}
5546

5647

5748
def steady_state_slice(step_times: list[float], warmup_frames: int) -> tuple[list[float], int]:
58-
"""Drop the leading ``warmup_frames`` cold-start steps, keeping >=1 frame.
59-
60-
Producer-side helper used by ``perf_runtime.py`` to exclude warmup at the
61-
source before aggregation. If ``warmup_frames`` would leave nothing, it is
62-
clamped to ``len(step_times) - 1`` so the aggregate never silently falls back
63-
to the full, cold-start-inclusive series (which would misreport non-steady
64-
numbers as steady-state).
49+
"""Drop leading cold-start steps while retaining at least one frame.
6550
6651
Args:
6752
step_times: Per-step wall times [s], in order.
6853
warmup_frames: Requested number of leading steps to discard.
6954
7055
Returns:
71-
``(measured_step_times, warmup_applied)`` where ``warmup_applied`` is the
72-
number of leading steps actually discarded (may be clamped below the
73-
request).
56+
Measured step times and the number of warmup frames actually removed.
7457
"""
75-
n = len(step_times)
76-
if n == 0:
58+
if not step_times:
7759
return [], 0
78-
warmup = max(0, min(warmup_frames, n - 1))
60+
warmup = max(0, min(warmup_frames, len(step_times) - 1))
7961
return list(step_times[warmup:]), warmup
8062

8163

82-
def load_info(info_path: Path) -> dict | None:
83-
"""Load the benchmark info JSON, or ``None`` if it is missing/unreadable."""
64+
def load_info(info_path: Path) -> object | None:
65+
"""Load benchmark JSON, or ``None`` when it is missing or unreadable."""
8466
try:
85-
data = json.loads(Path(info_path).read_text())
67+
return json.loads(Path(info_path).read_text())
8668
except Exception:
8769
return None
88-
return data if isinstance(data, dict) else None
8970

9071

9172
def is_runtime_bundle(data: Any) -> bool:
92-
"""Return True when ``data`` looks like a schema-v1 runtime/training bundle."""
73+
"""Return whether ``data`` looks like a schema-v1 runtime/training bundle."""
9374
return (
9475
isinstance(data, dict)
9576
and isinstance(data.get("run"), dict)
@@ -99,7 +80,7 @@ def is_runtime_bundle(data: Any) -> bool:
9980

10081

10182
def gpu_driver_version() -> str | None:
102-
"""Return the GPU driver version from nvidia-smi, or ``None`` if unavailable."""
83+
"""Return the GPU driver version from ``nvidia-smi``, when available."""
10384
try:
10485
result = subprocess.run(
10586
["nvidia-smi", "--query-gpu=driver_version", "--format=csv,noheader,nounits"],
@@ -116,7 +97,7 @@ def gpu_driver_version() -> str | None:
11697

11798

11899
def _current_gpu(bundle: dict) -> dict:
119-
"""Return the first GPU device dict from the hardware snapshot (or ``{}``)."""
100+
"""Return the first GPU device dictionary."""
120101
devices = _as_dict(bundle.get("hardware")).get("gpu_devices") or []
121102
return devices[0] if devices and isinstance(devices[0], dict) else {}
122103

@@ -129,53 +110,40 @@ def render_backend(bundle: dict) -> str | None:
129110

130111

131112
def fps_stats(bundle: dict) -> dict:
132-
"""Return ``raw_fps_{mean,std,min,max}`` from the bundle's runtime aggregates.
133-
134-
``mean``/``std``/``max`` come from ``total_fps``; ``min`` (worst steady-state
135-
frame) is recovered from the slowest step time. Percentile fields are not
136-
available in the schema and are intentionally omitted.
137-
"""
113+
"""Return steady-state FPS statistics from runtime aggregates."""
138114
runtime = _as_dict(bundle.get("runtime"))
139115
total_fps = _as_dict(runtime.get("total_fps"))
140-
iter_time = _as_dict(runtime.get("iteration_time_s"))
141-
steps_per_iter = runtime.get("steps_per_iteration") or _as_dict(bundle.get("run")).get("num_envs")
142-
143-
out: dict = {}
116+
iteration_time = _as_dict(runtime.get("iteration_time_s"))
117+
steps_per_iteration = runtime.get("steps_per_iteration") or _as_dict(bundle.get("run")).get("num_envs")
118+
output: dict[str, float] = {}
144119
if isinstance(total_fps.get("mean"), (int, float)):
145-
out["raw_fps_mean"] = float(total_fps["mean"])
120+
output["raw_fps_mean"] = float(total_fps["mean"])
146121
if isinstance(total_fps.get("std"), (int, float)):
147-
out["raw_fps_std"] = float(total_fps["std"])
122+
output["raw_fps_std"] = float(total_fps["std"])
148123
if isinstance(total_fps.get("peak"), (int, float)):
149-
out["raw_fps_max"] = float(total_fps["peak"])
150-
peak_step = iter_time.get("peak")
151-
if isinstance(peak_step, (int, float)) and peak_step > 0 and steps_per_iter:
152-
out["raw_fps_min"] = float(steps_per_iter) / float(peak_step)
153-
return out
124+
output["raw_fps_max"] = float(total_fps["peak"])
125+
peak_step = iteration_time.get("peak")
126+
if isinstance(peak_step, (int, float)) and peak_step > 0 and steps_per_iteration:
127+
output["raw_fps_min"] = float(steps_per_iteration) / float(peak_step)
128+
return output
154129

155130

156131
def startup_seconds(bundle: dict) -> float | None:
157-
"""Return total launch-to-first-step wall time [s] (sum of startup phases)."""
132+
"""Return total launch-to-first-step wall time [s]."""
158133
startup = _as_dict(_as_dict(bundle.get("runtime")).get("startup_time_s"))
159-
values = [v for v in startup.values() if isinstance(v, (int, float))]
134+
values = [value for value in startup.values() if isinstance(value, (int, float))]
160135
return float(sum(values)) if values else None
161136

162137

163138
def provenance(bundle: dict) -> dict:
164-
"""Return ``{hardware, software, git}`` for the runtime-compatibility contract.
165-
166-
``software`` is the bundle's typed ``versions`` map verbatim (its field names
167-
— ``isaaclab``/``isaacsim``/``torch``/``warp``/``isaaclab_physx``/
168-
``isaaclab_newton``/``newton``/``isaaclab_ov`` — match the contract policy
169-
paths, so the ``runtime_contract_hash`` is preserved across the migration).
170-
"""
139+
"""Return hardware, software, and Git runtime provenance."""
171140
versions = _as_dict(bundle.get("versions"))
172141
hardware_snapshot = _as_dict(bundle.get("hardware"))
173142
gpu = _current_gpu(bundle)
174-
175-
software = {k: v for k, v in versions.items() if v is not None and not k.startswith("git_")}
143+
software = {key: value for key, value in versions.items() if value is not None and not key.startswith("git_")}
176144
hardware = {
177-
k: v
178-
for k, v in {
145+
key: value
146+
for key, value in {
179147
"cpu_name": hardware_snapshot.get("cpu_name"),
180148
"cpu_physical_cores": hardware_snapshot.get("cpu_count"),
181149
"total_ram_gb": hardware_snapshot.get("ram_gb"),
@@ -184,7 +152,7 @@ def provenance(bundle: dict) -> dict:
184152
"gpu_total_memory_gb": gpu.get("mem_gb"),
185153
"gpu_compute_capability": gpu.get("compute_cap"),
186154
}.items()
187-
if v is not None
155+
if value is not None
188156
}
189157
git = {
190158
gate_key: versions[schema_key]
@@ -249,11 +217,11 @@ def runtime_resources(bundle: dict) -> dict:
249217
"system_ram_peak_mb": _gb_to_mb(ram.get("peak")),
250218
"cpu_util_pct": _pct(cpu_util.get("mean")),
251219
}
252-
return {k: v for k, v in diag.items() if v is not None}
220+
return {key: value for key, value in diag.items() if value is not None}
253221

254222

255223
def benchmark_info(bundle: dict) -> dict:
256-
"""Return the run's self-reported identity for launch/run drift checks."""
224+
"""Return the run's self-reported workload identity."""
257225
run = _as_dict(bundle.get("run"))
258226
config = _as_dict(run.get("config"))
259227
extra = _as_dict(bundle.get("extra"))
@@ -268,15 +236,11 @@ def benchmark_info(bundle: dict) -> dict:
268236
"render_backend": render_backend(bundle),
269237
"presets": config.get("presets") or [],
270238
}
271-
return {k: v for k, v in info.items() if v is not None}
239+
return {key: value for key, value in info.items() if value is not None}
272240

273241

274242
def project_runtime(bundle: dict) -> RuntimeSample | None:
275-
"""Project a runtime bundle into a typed :class:`~contracts.RuntimeSample`.
276-
277-
Returns ``None`` when ``bundle`` is not a valid schema-v1 runtime bundle, so the
278-
caller can degrade to a HARD_FAILURE (missing benchmark output).
279-
"""
243+
"""Project one RuntimeBundle into a canonical runtime sample."""
280244
if not is_runtime_bundle(bundle):
281245
return None
282246
stats = fps_stats(bundle)
@@ -299,3 +263,9 @@ def project_runtime(bundle: dict) -> RuntimeSample | None:
299263
provenance=provenance(bundle),
300264
runtime_resources=runtime_resources(bundle),
301265
)
266+
267+
268+
def project_sample(payload: object, *, warmup_frames: int = 0) -> RuntimeSample | None:
269+
"""Project the pinned RuntimeBundle payload into the canonical sample."""
270+
del warmup_frames # warmup is applied by perf_runtime.py before aggregation
271+
return project_runtime(payload) if is_runtime_bundle(payload) else None

0 commit comments

Comments
 (0)