Skip to content

Commit 28a0170

Browse files
author
Horde
committed
Scope stability qualification to the Newton buckets
The 2026-07-28 run collected clean evidence for the four PhysX-only buckets and none for the five that load Newton, which all crashed on `from newton.solvers import SolverNotifyFlags`. Re-running the full matrix would spend half the pool time re-measuring buckets that already worked. Restrict both the sampling and the qualification to the Newton-touching backends, cutting the run from 135 to 75 samples. The seeder already accepted a backend allowlist; runner_stability now takes the same list so the expected scope matches what was collected, instead of reporting the PhysX buckets as missing evidence and failing closed. An unknown backend key is rejected rather than silently dropped, since a typo would otherwise narrow the scope and yield a weaker verdict that still reads as qualified.
1 parent 90c1303 commit 28a0170

3 files changed

Lines changed: 96 additions & 1 deletion

File tree

.github/workflows/perf-smoke-runner-stability.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
# the commit out to five independent allocations so per-runner and run-to-run FPS
99
# spread can be measured separately. Qualification only reports a verdict; it
1010
# never writes baselines.
11+
#
12+
# Scope is the Newton-touching buckets. The PhysX-only buckets already produced
13+
# clean evidence on 2026-07-28; these five crashed on a Newton import and are the
14+
# ones still lacking a verdict. Halving the matrix also halves the pool time.
1115

1216
name: Performance Smoke - L40S Runner Stability
1317

@@ -29,6 +33,11 @@ concurrency:
2933
group: perf-smoke-runner-stability-${{ github.ref }}
3034
cancel-in-progress: false
3135

36+
env:
37+
# Every backend_key whose benchmark loads Newton. Covers five task/backend
38+
# buckets, since `newton` applies to both Cartpole and Velocity-Flat-G1.
39+
STABILITY_BACKENDS: "newton,newton_rtx_renderer,newton_newton_renderer,physx_newton_renderer"
40+
3241
jobs:
3342
wait_for_quiet_pool:
3443
name: Wait for initial gate and quiet pool
@@ -102,6 +111,9 @@ jobs:
102111
commit_count: "1"
103112
samples_per_commit: "3"
104113
tasks: "__ALL_TASKS__"
114+
# Literal because `with:` cannot read the `env` context. Kept in sync with
115+
# STABILITY_BACKENDS by test_staging_workflow_qualifies_only_newton_buckets.
116+
backends: "newton,newton_rtx_renderer,newton_newton_renderer,physx_newton_renderer"
105117
target_branch: perf-smoke/develop-staging
106118
strict_ancestry: true
107119
dry_run: true
@@ -156,6 +168,7 @@ jobs:
156168
done
157169
python3 tools/perf_smoke_test/runner_stability.py \
158170
"${RECORD_ARGS[@]}" \
171+
--backends "${STABILITY_BACKENDS}" \
159172
--gpu_model l40s \
160173
--expected_target_branch perf-smoke/develop-staging \
161174
--expected_commit "${GITHUB_SHA}" \

tools/perf_smoke_test/runner_stability.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,30 @@ def _noise_floor_for_task(task: TaskConfig, gpu_model: str) -> float:
124124
return 0.0
125125

126126

127+
def select_backends(tasks: list[TaskConfig], backends: str) -> list[TaskConfig]:
128+
"""Narrow the qualification scope to an explicit backend allowlist.
129+
130+
Args:
131+
tasks: Every task/backend bucket configured for the gate.
132+
backends: Comma-separated ``backend_key`` allowlist; empty keeps all.
133+
134+
Returns:
135+
The buckets whose backend is in the allowlist.
136+
137+
Raises:
138+
ValueError: If a requested backend matches no configured bucket, which
139+
would otherwise silently shrink the scope and weaken the verdict.
140+
"""
141+
requested = {backend.strip() for backend in backends.split(",") if backend.strip()}
142+
if not requested:
143+
return tasks
144+
configured = {task.backend_key for task in tasks}
145+
unknown = sorted(requested - configured)
146+
if unknown:
147+
raise ValueError(f"Unknown backend_key(s) {unknown}; configured backends are {sorted(configured)}")
148+
return [task for task in tasks if task.backend_key in requested]
149+
150+
127151
def configured_scope(
128152
tasks: list[TaskConfig],
129153
gpu_model: str,
@@ -589,6 +613,11 @@ def _parse_args() -> argparse.Namespace:
589613
parser = argparse.ArgumentParser(description=__doc__)
590614
parser.add_argument("--records", required=True, action="append", type=Path)
591615
parser.add_argument("--tasks_config", type=Path, default=Path(__file__).with_name("tasks.json"))
616+
parser.add_argument(
617+
"--backends",
618+
default="",
619+
help="Comma-separated backend_key allowlist to qualify (empty = every configured backend).",
620+
)
592621
parser.add_argument("--gpu_model", default="l40s")
593622
parser.add_argument("--expected_target_branch")
594623
parser.add_argument("--expected_commit")
@@ -606,7 +635,8 @@ def main() -> int:
606635
"""Run runner-pool stability qualification."""
607636
args = _parse_args()
608637
records = _load_records(args.records)
609-
expected, noise_floors = configured_scope(load_tasks(args.tasks_config), args.gpu_model)
638+
tasks = select_backends(load_tasks(args.tasks_config), args.backends)
639+
expected, noise_floors = configured_scope(tasks, args.gpu_model)
610640
report, markdown = build_report(
611641
records,
612642
expected_buckets=expected,

tools/perf_smoke_test/test/test_runner_stability.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,58 @@ def test_report_states_scope_and_decision() -> None:
312312
assert "runner-1" in markdown
313313

314314

315+
def test_backend_allowlist_narrows_the_qualified_scope() -> None:
316+
"""Only the requested backends are qualified, so unrelated buckets are not demanded."""
317+
tasks = runner_stability.load_tasks(_GATE_DIR / "tasks.json")
318+
319+
selected = runner_stability.select_backends(tasks, "newton,physx_newton_renderer")
320+
321+
assert {task.backend_key for task in selected} == {"newton", "physx_newton_renderer"}
322+
assert len(selected) < len(tasks)
323+
324+
325+
def test_empty_backend_allowlist_keeps_every_bucket() -> None:
326+
"""An empty allowlist must not silently narrow the scope."""
327+
tasks = runner_stability.load_tasks(_GATE_DIR / "tasks.json")
328+
329+
assert runner_stability.select_backends(tasks, "") == tasks
330+
331+
332+
def test_unknown_backend_is_rejected_rather_than_silently_dropped() -> None:
333+
"""A typo would otherwise shrink the scope and produce a weaker verdict unnoticed."""
334+
tasks = runner_stability.load_tasks(_GATE_DIR / "tasks.json")
335+
336+
with pytest.raises(ValueError, match="Unknown backend_key"):
337+
runner_stability.select_backends(tasks, "newton,nwton")
338+
339+
340+
def test_staging_workflow_qualifies_only_newton_buckets() -> None:
341+
"""The sampled backends and the qualified backends must be the same set."""
342+
repo_root = Path(__file__).resolve().parents[3]
343+
workflow = yaml.safe_load(
344+
(repo_root / ".github/workflows/perf-smoke-runner-stability.yaml").read_text(encoding="utf-8")
345+
)
346+
347+
declared = workflow["env"]["STABILITY_BACKENDS"]
348+
sampled = workflow["jobs"]["stability_sample"]["with"]["backends"]
349+
assert sampled == declared, "the seeder's backend list drifted from STABILITY_BACKENDS"
350+
351+
report_step = next(
352+
step for step in workflow["jobs"]["qualify"]["steps"] if step.get("name") == "Build qualification report"
353+
)
354+
assert '--backends "${STABILITY_BACKENDS}"' in report_step["run"]
355+
356+
# Exactly the buckets that crashed on 2026-07-28, and no PhysX-only bucket.
357+
tasks = runner_stability.select_backends(runner_stability.load_tasks(_GATE_DIR / "tasks.json"), declared)
358+
assert {(task.task_id, task.backend_key) for task in tasks} == {
359+
("Isaac-Cartpole-Direct", "newton"),
360+
("Isaac-Velocity-Flat-G1", "newton"),
361+
("Isaac-Reorient-Cube-Shadow-Camera-Benchmark-Direct", "newton_rtx_renderer"),
362+
("Isaac-Reorient-Cube-Shadow-Camera-Benchmark-Direct", "newton_newton_renderer"),
363+
("Isaac-Reorient-Cube-Shadow-Camera-Benchmark-Direct", "physx_newton_renderer"),
364+
}
365+
366+
315367
def test_staging_workflow_fans_out_complete_independent_evidence() -> None:
316368
"""One staging merge automatically gathers and qualifies five allocations."""
317369
repo_root = Path(__file__).resolve().parents[3]

0 commit comments

Comments
 (0)