# device_monitors

Python API: `modelconverter.utils.device_monitors`

Sampling of a device's hardware counters during a benchmark.

Benchmarking a converted model on real hardware says how fast it runs but not what it costs. The monitor here fills that gap:
while the benchmark is running, it polls the power, RAM, CPU, DSP and thermal counters the device exposes over its shell, and
reports their aggregates afterwards. It is used by the RVC4 benchmark, which also takes an idle baseline to compare against.

## Classes

### DeviceMonitor

Background sampler of a device's hardware counters.

Reads the power, RAM, CPU, DSP and temperature counters over the device's shell at a fixed interval from a daemon thread, keeping
every sample so that it can be aggregated afterwards. A hardware monitor the device does not expose is detected once, up front,
and its power counter is skipped from then on; the other counters are attempted on every sample and simply yield nothing when they
fail.

Doubles as a context manager: entering the `with` block starts the sampling thread and leaving it stops the thread.

#### Methods

##### init

```python
def __init__(device_handler: DeviceHandler, interval: float = 0.5, model: Literal['4d', '4s', '4lite'] = '4lite'):
```

Initialize the monitor and probe the available counters.

The probing happens right away: the hardware monitors and the DSP utility are looked for over the device shell. A hardware monitor
that is missing is left out of the sampling, while the outcome of the DSP probe is only recorded.

Parameters

 * `device_handler` (`DeviceHandler`): Handler used to run shell commands on the device.
 * `interval` (`float`): Delay in seconds between two samples.
 * `model` (`Literal['4d', '4s', '4lite']`): Device model being monitored. Recorded only.

##### get_idle_measurements

```python
def get_idle_measurements(t: float = 5) -> dict[str, float | None]:
```

Sample the counters while the device is idle.

Gives the baseline the measurements taken under load are compared against.

Parameters

 * `t` (`float`): How long to sample for, in seconds.

Returns

 * `dict[str, float | None]`: The aggregated counters, each key prefixed with `idle_`.

##### get_stats

```python
def get_stats(self) -> dict[str, float | None]:
```

Aggregate the samples collected so far.

The DSP frequency residencies and the DSP power collapse counter are summed, every other counter is averaged.

Returns

 * `dict[str, float | None]`: Mapping of counter name to its aggregate, with `None` for a counter that has no samples.

##### start

```python
def start(self):
```

Start the background sampling thread.

If the monitor is already running, this is a no-op.

##### stop

```python
def stop(self):
```

Stop the background sampling thread and wait for it to finish.
