diff --git a/cpu_governor_auto_adjust/helper.py b/cpu_governor_auto_adjust/helper.py new file mode 100644 index 0000000..182686e --- /dev/null +++ b/cpu_governor_auto_adjust/helper.py @@ -0,0 +1,10 @@ +def format_time_ns(elapsed_ns: float) -> str: + """Format elapsed time dynamically in an appropriate unit.""" + if elapsed_ns < 1_000: # Less than 1 µs + return f"{elapsed_ns} ns" # Nanoseconds + elif elapsed_ns < 1_000_000: # Less than 1 ms + return f"{elapsed_ns / 1_000:.2f} µs" # Microseconds + elif elapsed_ns < 1_000_000_000: # Less than 1 second + return f"{elapsed_ns / 1_000_000:.2f} ms" # Milliseconds + else: + return f"{elapsed_ns / 1_000_000_000:.2f} s" # Seconds diff --git a/cpu_governor_auto_adjust/schedule.py b/cpu_governor_auto_adjust/schedule.py index bc6da82..2fefde8 100644 --- a/cpu_governor_auto_adjust/schedule.py +++ b/cpu_governor_auto_adjust/schedule.py @@ -2,11 +2,12 @@ from cpu_governor_auto_adjust.app_class import AppClass from cpu_governor_auto_adjust.config import Config from cpu_governor_auto_adjust.trigger import Trigger from cpu_governor_auto_adjust.governor import GovernorControl, Governor, _governor_list +from cpu_governor_auto_adjust.helper import format_time_ns import asyncio import signal from asyncio import Task from functools import cached_property, partial -from time import monotonic +from time import monotonic_ns as monotonic from typing import Callable, Any, Optional @@ -60,8 +61,7 @@ class TriggerScheduler(AppClass): self.log.debug("scheduling task: %s", task.__name__) start = monotonic() await self.loop.create_task(task(*args)) - end = monotonic() - self.log.debug("task %s finished in %.3f ms", task.__name__, (end - start) * 1000) + self.log.debug("task %s finished in %s", task.__name__, format_time_ns(monotonic() - start)) async def run(self) -> None: """Run the scheduler and keep it alive until stopped.""" diff --git a/cpu_governor_auto_adjust/trigger.py b/cpu_governor_auto_adjust/trigger.py index 7f0efcf..90cca7c 100644 --- a/cpu_governor_auto_adjust/trigger.py +++ b/cpu_governor_auto_adjust/trigger.py @@ -3,9 +3,10 @@ from cpu_governor_auto_adjust.config import Config, TriggerTuple from cpu_governor_auto_adjust.exceptions import MissingConfig, TriggerImportError from cpu_governor_auto_adjust.governor import Governor, _governor_list from functools import cached_property -from time import monotonic +from time import monotonic_ns as monotonic from datetime import datetime import asyncio +from cpu_governor_auto_adjust.helper import format_time_ns class Trigger(AppClass): @@ -15,16 +16,25 @@ class Trigger(AppClass): self.log.setLevel(self.config.loglevel.upper()) self._timestamp_last_statistics = monotonic() self._total_consumed_time = 0 + self._minimum_consumed_time = 10000000000 + self._maximum_consumed_time = 0 self.runs = 0 def __hash__(self): return super().__hash__(hash(self.name)) @property - def average_consumed_time(self) -> str: + def consumed_time(self) -> dict[str, str]: + _dict = { + "total": format_time_ns(self._total_consumed_time), + "minimum": format_time_ns(self._minimum_consumed_time), + "maximum": format_time_ns(self._maximum_consumed_time), + "average": 0, + } if self.runs == 0: - return 0 - return f"{(self._total_consumed_time / self.runs) * 1_000_000:.3f} µs" + return _dict + _dict["average"] = format_time_ns(self._total_consumed_time / self.runs) + return _dict def log_statistics(self): class_vars = {} @@ -50,8 +60,7 @@ class Trigger(AppClass): value = str(value) class_vars[key] = value - end = monotonic() - self.log.info("info and statistics [time taken: %.3fµs]: %s", (end - start) * 1_000_000, class_vars) + self.log.info("info and statistics [time taken: %s]: %s", format_time_ns(monotonic() - start), class_vars) @property def trigger_state(self) -> str: @@ -91,13 +100,15 @@ class Trigger(AppClass): while True: start = monotonic() self.log.debug("run trigger code of %s", self.__class__.__name__) - if self._timestamp_last_statistics + int(self._config.statistics_interval_in_seconds) < monotonic(): + if self._timestamp_last_statistics + (int(self._config.statistics_interval_in_seconds) * 1_000_000_000) < start: self.log_statistics() - self._timestamp_last_statistics = monotonic() + self._timestamp_last_statistics = start self.trigger_code() end = monotonic() self._total_consumed_time += end - start + self._minimum_consumed_time = min(self._minimum_consumed_time, end - start) + self._maximum_consumed_time = max(self._maximum_consumed_time, end - start) self.runs += 1 - self.log.debug("trigger state: %s, finished in %.3f µs", self.trigger_state, (end - start) * 1_000_000) + self.log.debug("trigger state: %s, finished in %s", self.trigger_state, format_time_ns(end - start)) await asyncio.sleep(self.config.interval_in_seconds) \ No newline at end of file diff --git a/cpu_governor_auto_adjust/triggers/cpu_load.py b/cpu_governor_auto_adjust/triggers/cpu_load.py index 5496838..679e16b 100644 --- a/cpu_governor_auto_adjust/triggers/cpu_load.py +++ b/cpu_governor_auto_adjust/triggers/cpu_load.py @@ -51,13 +51,13 @@ class CpuLoadTrigger(Trigger): return any(load >= threshold for load, threshold in zip(self.current_load, self.high_threshold)) @property - def current_load_average_over_under_threshold(self) -> bool: + def current_load_average_under_low_threshold(self) -> bool: return any(load <= threshold for load, threshold in zip(self.current_load, self.low_threshold)) def trigger_code(self) -> None: current_active = self._active new_high_load_active = self.current_load_average_over_high_threshold - new_low_load_active = self.current_load_average_over_under_threshold + new_low_load_active = self.current_load_average_under_low_threshold if not current_active and new_high_load_active: self.log.info( diff --git a/pyproject.toml b/pyproject.toml index 79534c9..1d2f7d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "cpu_governor_auto_adjust" -version = "0.5.12" +version = "0.5.17" description = "This application has been developed to automatically change cpu governor based on certain triggers." authors = [ { name = "Martin Reurekas", email = "martin@semrks.nl" }