Source code for libretro.drivers.perf.default

"""
:class:`.PerfDriver` implementation backed by :mod:`time` for timestamps.

.. seealso::

    :class:`.PerfDriver`
        The protocol this driver implements.
"""

from collections.abc import Mapping
from time import process_time_ns, time_ns
from typing import override

from libretro.api.perf import CpuFeatures, retro_perf_counter

from .driver import PerfDriver


[docs] class DefaultPerfDriver(PerfDriver): """ The default :class:`.PerfDriver` used when a :class:`.Session` is given no other. Backed by the standard :mod:`time` module (:func:`time.time_ns` for wall-clock and :func:`time.process_time_ns` for the perf counter). .. seealso:: :class:`.PerfDriver` The protocol this class implements. """
[docs] def __init__(self): """Initialize the driver with an empty performance-counter registry.""" super().__init__() self._perf_counters: dict[bytes, retro_perf_counter] = {}
[docs] def __del__(self): """Drop references to the registered counters before this driver is collected.""" self._perf_counters.clear()
# The dict contains references to core-owned data; # clear them here in case someone else has taken ownership of the dict. @property def perf_counters(self) -> Mapping[bytes, retro_perf_counter]: """A live mapping of registered performance-counter identifiers to their structs.""" return self._perf_counters
[docs] @override def get_time_usec(self) -> int: return time_ns() // 1000
[docs] @override def get_perf_counter(self) -> int: return process_time_ns()
[docs] @override def get_cpu_features(self) -> CpuFeatures: # TODO: Use Cython to wrap libretro-common's get_cpu_features raise NotImplementedError("get_cpu_features is not implemented")
[docs] @override def perf_register(self, counter: retro_perf_counter) -> None: if not counter.ident: raise ValueError("counter.ident must not be NULL") if counter.start: raise ValueError( f"Expected counter.start to be zero upon registration, got {counter.start}" ) if counter.total: raise ValueError( f"Expected counter.total to be zero upon registration, got {counter.total}" ) if counter.call_cnt: raise ValueError( f"Expected counter.call_cnt to be zero upon registration, got {counter.call_cnt}" ) if counter.registered: raise ValueError( f"Expected counter.registered to be false upon registration, got {counter.registered}" ) ident = counter.ident if ident in self._perf_counters: raise ValueError(f"counter with ident {ident} already exists") counter.registered = True self._perf_counters[ident] = counter
[docs] @override def perf_start(self, counter: retro_perf_counter) -> None: assert counter.ident in self._perf_counters counter.call_cnt += 1 counter.start = self.get_perf_counter()
[docs] @override def perf_stop(self, counter: retro_perf_counter) -> None: assert counter.ident in self._perf_counters counter.total += self.get_perf_counter() - counter.start
[docs] @override def perf_log(self) -> None: for counter in self._perf_counters.values(): if counter.call_cnt: print(f"{counter.ident}: {counter.total / counter.call_cnt} ns") else: print(f"{counter.ident}: Not called")
__all__ = ["DefaultPerfDriver"]