# Copyright (c) 2022-2025, The Isaac Lab Project Developers.# All rights reserved.## SPDX-License-Identifier: BSD-3-Clause"""Sub-module for a timer class that can be used for performance measurements."""from__future__importannotationsimporttimefromcontextlibimportContextDecoratorfromtypingimportAny,ClassVar
[docs]classTimerError(Exception):"""A custom exception used to report errors in use of :class:`Timer` class."""pass
[docs]classTimer(ContextDecorator):"""A timer for performance measurements. A class to keep track of time for performance measurement. It allows timing via context managers and decorators as well. It uses the `time.perf_counter` function to measure time. This function returns the number of seconds since the epoch as a float. It has the highest resolution available on the system. As a regular object: .. code-block:: python import time from isaaclab.utils.timer import Timer timer = Timer() timer.start() time.sleep(1) print(1 <= timer.time_elapsed <= 2) # Output: True time.sleep(1) timer.stop() print(2 <= stopwatch.total_run_time) # Output: True As a context manager: .. code-block:: python import time from isaaclab.utils.timer import Timer with Timer() as timer: time.sleep(1) print(1 <= timer.time_elapsed <= 2) # Output: True Reference: https://gist.github.com/sumeet/1123871 """timing_info:ClassVar[dict[str,float]]=dict()"""Dictionary for storing the elapsed time per timer instances globally. This dictionary logs the timer information. The keys are the names given to the timer class at its initialization. If no :attr:`name` is passed to the constructor, no time is recorded in the dictionary. """
[docs]def__init__(self,msg:str|None=None,name:str|None=None):"""Initializes the timer. Args: msg: The message to display when using the timer class in a context manager. Defaults to None. name: The name to use for logging times in a global dictionary. Defaults to None. """self._msg=msgself._name=nameself._start_time=Noneself._stop_time=Noneself._elapsed_time=None
def__str__(self)->str:"""A string representation of the class object. Returns: A string containing the elapsed time. """returnf"{self.time_elapsed:0.6f} seconds"""" Properties """@propertydeftime_elapsed(self)->float:"""The number of seconds that have elapsed since this timer started timing. Note: This is used for checking how much time has elapsed while the timer is still running. """returntime.perf_counter()-self._start_time@propertydeftotal_run_time(self)->float:"""The number of seconds that elapsed from when the timer started to when it ended."""returnself._elapsed_time""" Operations """
[docs]defstart(self):"""Start timing."""ifself._start_timeisnotNone:raiseTimerError("Timer is running. Use .stop() to stop it")self._start_time=time.perf_counter()
[docs]defstop(self):"""Stop timing."""ifself._start_timeisNone:raiseTimerError("Timer is not running. Use .start() to start it")self._stop_time=time.perf_counter()self._elapsed_time=self._stop_time-self._start_timeself._start_time=Noneifself._name:Timer.timing_info[self._name]=self._elapsed_time
""" Context managers """def__enter__(self)->Timer:"""Start timing and return this `Timer` instance."""self.start()returnselfdef__exit__(self,*exc_info:Any):"""Stop timing."""self.stop()# print messageifself._msgisnotNone:print(self._msg,f": {self._elapsed_time:0.6f} seconds")""" Static Methods """
[docs]@staticmethoddefget_timer_info(name:str)->float:"""Retrieves the time logged in the global dictionary based on name. Args: name: Name of the the entry to be retrieved. Raises: TimerError: If name doesn't exist in the log. Returns: A float containing the time logged if the name exists. """ifnamenotinTimer.timing_info:raiseTimerError(f"Timer {name} does not exist")returnTimer.timing_info.get(name)