Skip to content

Commit dcc2ab2

Browse files
author
Horde
committed
Fix current benchmark reconstruction
Support the public benchmark namespace and select concrete migrated physics presets so reconstructed current commits execute the requested backend.
1 parent 96db8a8 commit dcc2ab2

9 files changed

Lines changed: 122 additions & 44 deletions

File tree

tools/perf_bisection/src/isaaclab_bisection/launch_config.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,16 @@ def workload_contract(config: dict[str, Any]) -> dict[str, Any]:
5656

5757
def hydra_args_for_task(task: TaskConfig) -> list[str]:
5858
"""Return Hydra args used by the current local/CI benchmark launch path."""
59-
presets: list[str] = []
60-
if task.physics_backend == "newton":
61-
presets.append("newton_mjwarp")
59+
args: list[str] = []
60+
if task.physics_backend == "physx":
61+
args.append("physics=isaacsim_physx")
62+
elif task.physics_backend == "newton":
63+
args.append("physics=newton_mjwarp")
6264
if task.render_backend:
63-
presets.append(task.render_backend)
65+
args.append(f"renderer={task.render_backend}")
6466
if task.render_backend == "newton_renderer":
65-
presets.append("rgb")
66-
return [f"presets={','.join(presets)}"] if presets else []
67+
args.append("presets=rgb")
68+
return args
6769

6870

6971
def task_to_launch_config(

tools/perf_bisection/tests/test_bisect_runner.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,24 @@ def test_default_hydra_args_used_when_none_given(self) -> None:
851851
args = argparse.Namespace(hydra_arg=[])
852852
resolved = runner._resolve_hydra_args(args, task)
853853
assert isinstance(resolved, list)
854-
assert any("newton" in item for item in resolved)
854+
assert resolved == ["physics=newton_mjwarp"]
855+
856+
def test_physx_backend_selects_isaac_sim_physx(self) -> None:
857+
task = runner._build_task(
858+
argparse.Namespace(
859+
task_id="Isaac-Cartpole-Direct",
860+
backend_key="physx",
861+
tasks_json=None,
862+
num_envs=None,
863+
num_frames=None,
864+
warmup_frames=None,
865+
seed=None,
866+
camera_resolution=None,
867+
timeout_minutes=None,
868+
)
869+
)
870+
871+
assert runner._resolve_hydra_args(argparse.Namespace(hydra_arg=[]), task) == ["physics=isaacsim_physx"]
855872

856873

857874
class TestEngineForwardsTaskSpec:

tools/perf_bisection/tests/test_tooling.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77

88
from __future__ import annotations
99

10+
import importlib.util
1011
import json
1112
import shutil
1213
import subprocess
1314
import sys
1415
from pathlib import Path
15-
from types import SimpleNamespace
16+
from types import ModuleType, SimpleNamespace
1617

1718
import pytest
1819

@@ -38,6 +39,23 @@
3839
)
3940

4041

42+
def _load_tooling_capability_module() -> ModuleType:
43+
path = _REPO_ROOT / "tools" / "perf_smoke_test" / "tooling_capability.py"
44+
spec = importlib.util.spec_from_file_location("test_tooling_capability", path)
45+
assert spec is not None
46+
assert spec.loader is not None
47+
module = importlib.util.module_from_spec(spec)
48+
spec.loader.exec_module(module)
49+
return module
50+
51+
52+
def _fake_module(name: str, attributes: tuple[str, ...] = ()) -> ModuleType:
53+
module = ModuleType(name)
54+
for attribute in attributes:
55+
setattr(module, attribute, object())
56+
return module
57+
58+
4159
@pytest.fixture
4260
def committed_tooling_repo(tmp_path: Path) -> tuple[Path, str]:
4361
"""Create a repository whose committed tooling can be archived by SHA."""
@@ -102,6 +120,27 @@ def test_capability_command_uses_pinned_tooling(tmp_path: Path) -> None:
102120
assert command[-1] == str(tmp_path / "artifacts" / "tooling_capability.json")
103121

104122

123+
@pytest.mark.parametrize("benchmark_namespace", ["isaaclab.benchmark", "isaaclab.test.benchmark"])
124+
def test_tooling_capability_accepts_current_and_legacy_benchmark_namespaces(
125+
monkeypatch: pytest.MonkeyPatch, benchmark_namespace: str
126+
) -> None:
127+
benchmark_attributes = ("BaseIsaacLabBenchmark", "BenchmarkMonitor", "builders", "capture", "stepping")
128+
modules = {
129+
"isaaclab": _fake_module("isaaclab"),
130+
"isaaclab.app": _fake_module("isaaclab.app", ("AppLauncher", "launch_simulation")),
131+
benchmark_namespace: _fake_module(benchmark_namespace, benchmark_attributes),
132+
f"{benchmark_namespace}.schema": _fake_module(f"{benchmark_namespace}.schema", ("StartupTime",)),
133+
"isaaclab_tasks": _fake_module("isaaclab_tasks"),
134+
"isaaclab_tasks.utils": _fake_module("isaaclab_tasks.utils", ("setup_preset_cli", "resolve_task_config")),
135+
}
136+
for name, module in modules.items():
137+
monkeypatch.setitem(sys.modules, name, module)
138+
139+
capability = _load_tooling_capability_module()
140+
141+
assert capability.check_capabilities() == []
142+
143+
105144
def test_resolved_plan_pins_task_metric_and_warmups() -> None:
106145
resolved = resolve_tooling_plan(_inline_plan(), _REPO_ROOT, tooling_ref="WORKTREE")
107146

tools/perf_smoke_test/benchmark_result_adapter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
"""Adapter: schema-v1 ``RuntimeBundle`` JSON -> perf-gate normalized fields.
77
88
``perf_runtime.py`` emits a
9-
:class:`~isaaclab.test.benchmark.schema.RuntimeBundle` (Isaac Lab benchmark
9+
:class:`~isaaclab.benchmark.schema.RuntimeBundle` (Isaac Lab benchmark
1010
refactor Part 1, PR #6197) serialized by
11-
:func:`~isaaclab.test.benchmark.serialize.write_bundle_file`. This module is the
11+
:func:`~isaaclab.benchmark.serialize.write_bundle_file`. This module is the
1212
single point that reads that JSON and projects it into the flat
1313
``provenance`` / ``runtime_resources`` / fps / ``benchmark_info`` shapes the gate's
1414
:mod:`oracle` and :mod:`build_bench_result` consume, replacing the legacy

tools/perf_smoke_test/contracts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
"""Typed contracts for the perf-gate's own artifacts (schema v1).
77
8-
The gate's analogue of :mod:`isaaclab.test.benchmark.schema`: the fields the gate
8+
The gate's analogue of :mod:`isaaclab.benchmark.schema`: the fields the gate
99
computes on are typed dataclass attributes, while open pass-through sub-structures
1010
(``benchmark_info``, ``provenance``, ``launch_config``, the compatibility contracts)
1111
stay as ``dict`` fields — mirroring ``RuntimeBundle``'s typed fields + ``extra: dict``.

tools/perf_smoke_test/dev/stub_benchmark.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66

77
"""Local-testing stub that fakes a ``perf_runtime.py`` run without a GPU/sim.
88
9-
Emits a schema-v1 :class:`~isaaclab.test.benchmark.schema.RuntimeBundle`
9+
Emits a schema-v1 :class:`~isaaclab.benchmark.schema.RuntimeBundle`
1010
(``benchmark_runtime_{task}_{stamp}.json``) identical in shape to what the real
1111
driver writes, so ``build_bench_result.py`` -> ``benchmark_result_adapter`` ->
1212
``oracle`` can be exercised end-to-end offline. Uses the real
13-
``isaaclab.test.benchmark`` builders/serialize (pure-Python, no GPU), so it stays
13+
``isaaclab.benchmark`` builders/serialize (pure-Python, no GPU), so it stays
1414
in lockstep with the schema; run it with the Isaac Lab Python env.
1515
"""
1616

@@ -26,8 +26,8 @@
2626

2727
from backend_identity import split_backend_key # noqa: E402
2828

29-
from isaaclab.test.benchmark import builders, serialize # noqa: E402
30-
from isaaclab.test.benchmark.schema import ( # noqa: E402
29+
from isaaclab.benchmark import builders, serialize # noqa: E402
30+
from isaaclab.benchmark.schema import ( # noqa: E402
3131
GpuDeviceInfo,
3232
Hardware,
3333
MeanStd,

tools/perf_smoke_test/launch_config.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,16 @@ def workload_contract(config: dict[str, Any]) -> dict[str, Any]:
5555

5656
def hydra_args_for_task(task: TaskConfig) -> list[str]:
5757
"""Return Hydra args used by the current local/CI benchmark launch path."""
58-
presets: list[str] = []
59-
if task.physics_backend == "newton":
60-
presets.append("newton_mjwarp")
58+
args: list[str] = []
59+
if task.physics_backend == "physx":
60+
args.append("physics=isaacsim_physx")
61+
elif task.physics_backend == "newton":
62+
args.append("physics=newton_mjwarp")
6163
if task.render_backend:
62-
presets.append(task.render_backend)
64+
args.append(f"renderer={task.render_backend}")
6365
if task.render_backend == "newton_renderer":
64-
presets.append("rgb")
65-
return [f"presets={','.join(presets)}"] if presets else []
66+
args.append("presets=rgb")
67+
return args
6668

6769

6870
def task_to_launch_config(

tools/perf_smoke_test/perf_runtime.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
88
Thin wrapper over the merged Isaac Lab benchmark core (benchmark refactor
99
Part 1/5, PR #6197) that produces a schema-v1
10-
:class:`~isaaclab.test.benchmark.schema.RuntimeBundle`. It intentionally does
10+
:class:`~isaaclab.benchmark.schema.RuntimeBundle`. It intentionally does
1111
**not** depend on the still-unmerged ``scripts/benchmarks/runtime.py`` (Part 2/5,
1212
PR #6198); it only imports the stable, merged building blocks
13-
(:mod:`~isaaclab.test.benchmark.stepping`, :mod:`~isaaclab.test.benchmark.builders`,
14-
:mod:`~isaaclab.test.benchmark.capture`) so the perf gate can adopt the typed
13+
(:mod:`~isaaclab.benchmark.stepping`, :mod:`~isaaclab.benchmark.builders`,
14+
:mod:`~isaaclab.benchmark.capture`) so the perf gate can adopt the typed
1515
bundle schema before the rest of the refactor lands.
1616
1717
Difference from the upstream runtime script: the perf gate discards a
@@ -85,8 +85,13 @@
8585
import gymnasium as gym
8686

8787
from isaaclab.app import launch_simulation
88-
from isaaclab.test.benchmark import BaseIsaacLabBenchmark, BenchmarkMonitor, builders, capture, stepping
89-
from isaaclab.test.benchmark.schema import StartupTime
88+
89+
try:
90+
from isaaclab.benchmark import BaseIsaacLabBenchmark, BenchmarkMonitor, builders, capture, stepping
91+
from isaaclab.benchmark.schema import StartupTime
92+
except ModuleNotFoundError:
93+
from isaaclab.test.benchmark import BaseIsaacLabBenchmark, BenchmarkMonitor, builders, capture, stepping
94+
from isaaclab.test.benchmark.schema import StartupTime
9095

9196
import isaaclab_tasks # noqa: F401
9297
from isaaclab_tasks.utils import resolve_task_config

tools/perf_smoke_test/tooling_capability.py

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
from pathlib import Path
1414

1515
TOOLING_INCOMPATIBLE_EXIT_CODE = 86
16+
_BENCHMARK_API_ALTERNATIVES = (
17+
("isaaclab.benchmark", "isaaclab.benchmark.schema"),
18+
("isaaclab.test.benchmark", "isaaclab.test.benchmark.schema"),
19+
)
1620

1721

1822
def _parse_args() -> argparse.Namespace:
@@ -21,27 +25,36 @@ def _parse_args() -> argparse.Namespace:
2125
return parser.parse_args()
2226

2327

28+
def _check_module(module_name: str, names: tuple[str, ...]) -> list[str]:
29+
"""Return missing names or an import failure for one module."""
30+
missing: list[str] = []
31+
try:
32+
module = __import__(module_name, fromlist=list(names))
33+
except Exception as exc: # noqa: BLE001 - the compatibility report records the exact import failure
34+
return [f"{module_name}: {type(exc).__name__}: {exc}"]
35+
for name in names:
36+
if not hasattr(module, name):
37+
missing.append(f"{module_name}.{name}")
38+
return missing
39+
40+
2441
def check_capabilities() -> list[str]:
2542
"""Return missing API descriptions required by ``perf_runtime.py``."""
26-
missing: list[str] = []
27-
checks = (
28-
("isaaclab.app", ("AppLauncher", "launch_simulation")),
29-
(
30-
"isaaclab.test.benchmark",
43+
missing = _check_module("isaaclab.app", ("AppLauncher", "launch_simulation"))
44+
missing.extend(_check_module("isaaclab_tasks.utils", ("setup_preset_cli", "resolve_task_config")))
45+
46+
benchmark_failures: list[str] = []
47+
for benchmark_module, schema_module in _BENCHMARK_API_ALTERNATIVES:
48+
alternative_missing = _check_module(
49+
benchmark_module,
3150
("BaseIsaacLabBenchmark", "BenchmarkMonitor", "builders", "capture", "stepping"),
32-
),
33-
("isaaclab.test.benchmark.schema", ("StartupTime",)),
34-
("isaaclab_tasks.utils", ("setup_preset_cli", "resolve_task_config")),
35-
)
36-
for module_name, names in checks:
37-
try:
38-
module = __import__(module_name, fromlist=list(names))
39-
except Exception as exc: # noqa: BLE001 - the compatibility report records the exact import failure
40-
missing.append(f"{module_name}: {type(exc).__name__}: {exc}")
41-
continue
42-
for name in names:
43-
if not hasattr(module, name):
44-
missing.append(f"{module_name}.{name}")
51+
)
52+
alternative_missing.extend(_check_module(schema_module, ("StartupTime",)))
53+
if not alternative_missing:
54+
break
55+
benchmark_failures.extend(alternative_missing)
56+
else:
57+
missing.extend(benchmark_failures)
4558
return missing
4659

4760

0 commit comments

Comments
 (0)