Source code for isaaclab.benchmark.benchmark_monitor

# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""Background monitoring thread for continuous benchmark recording during blocking operations."""

import threading
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from .benchmark_core import BaseIsaacLabBenchmark


[docs] class BenchmarkMonitor: """Background thread that periodically updates benchmark recorders. This utility enables continuous system resource monitoring during blocking RL training loops (RSL-RL, RL-Games) where `update_manual_recorders()` would otherwise only be called once after training completes. Usage: with BenchmarkMonitor(benchmark, interval=1.0): runner.learn(...) # Blocking training call """
[docs] def __init__(self, benchmark: "BaseIsaacLabBenchmark", interval: float = 1.0): """Initialize the benchmark monitor. Args: benchmark: The benchmark instance to monitor. interval: Time between recorder updates in seconds. Defaults to 1.0. """ self._benchmark = benchmark self._interval = interval self._stop_event = threading.Event() self._thread: threading.Thread | None = None self._exception: Exception | None = None
def _monitor_loop(self) -> None: """Background loop that updates recorders at the specified interval.""" while not self._stop_event.is_set(): try: self._benchmark.update_manual_recorders() except Exception as e: self._exception = e # Log but don't crash - monitoring failure shouldn't stop training print(f"[BenchmarkMonitor] Warning: update failed: {e}") # Wait for interval or stop signal self._stop_event.wait(timeout=self._interval)
[docs] def start(self) -> None: """Start the monitoring thread.""" if self._thread is not None and self._thread.is_alive(): return # Already running self._stop_event.clear() self._exception = None self._thread = threading.Thread(target=self._monitor_loop, daemon=True) self._thread.start()
[docs] def stop(self) -> None: """Stop the monitoring thread and wait for it to finish.""" self._stop_event.set() if self._thread is not None: self._thread.join(timeout=self._interval + 1.0) self._thread = None
def __enter__(self) -> "BenchmarkMonitor": """Start monitoring when entering context.""" self.start() return self def __exit__(self, exc_type, exc_val, exc_tb) -> None: """Stop monitoring when exiting context.""" self.stop()