diff --git a/metrics/handlers.py b/metrics/handlers.py index 108bb65..e4f50b8 100644 --- a/metrics/handlers.py +++ b/metrics/handlers.py @@ -1,16 +1,12 @@ -import platform -import cpuinfo - from rich.console import Console -from rich.panel import Panel -from rich.table import Table -from rich.text import Text from argenta.command.models import Command from argenta.response import Response from argenta.router import Router from .benchmarks.core.models import BenchmarkGroupResult from .benchmarks.entity import benchmarks as registered_benchmarks +from .services.report_generator import ReportGenerator +from .services.system_info_reader import get_system_info console = Console() router = Router(title="Metrics commands:") @@ -18,48 +14,14 @@ router = Router(title="Metrics commands:") @router.command(Command("all-print", description="Print all benchmarks results")) def all_print_handler(_: Response) -> None: - cpu_info = cpuinfo.get_cpu_info() - gpu_info = get_gpu_info() - os_info = get_kernel_version() - - table = Table(show_header=True, header_style="bold cyan", border_style="blue", show_lines=True) - table.add_column("Parameter", style="green") - table.add_column("Value", style="yellow") - - table.add_row("OS", platform.system()) - table.add_row("OS Name", os_info['product_name']) - table.add_row("OS Kernel Version", os_info['kernel_version']) - table.add_row("Architecture", cpu_info['arch']) - table.add_row("CPU", cpu_info['brand_raw']) - table.add_row("GPU", gpu_info) - table.add_row("Python Version", cpu_info['python_version']) - table.add_row("Python Implementation", platform.python_implementation()) - - header_text = Text("SYSTEM INFO", style="bold magenta") - console.print(Panel(header_text, expand=False, border_style="magenta")) - console.print(table, end="\n\n") - + report_generator = ReportGenerator(get_system_info()) + console.print(report_generator.generate_system_info_header()) + console.print(report_generator.generate_system_info_table()) + return type_grouped_benchmarks: list[BenchmarkGroupResult] = registered_benchmarks.run_benchmarks_grouped_by_type() - - for results in type_grouped_benchmarks: - header_text = Text(f"TYPE: {results.type_.upper()} ; ITERATIONS: {results.iterations} ; ALL TIME IN MS", style="bold magenta") - console.print(Panel(header_text, expand=False, border_style="magenta")) - - table = Table(show_header=True, header_style="bold cyan", border_style="blue", show_lines=True) - table.add_column("Description", style="dim") - table.add_column("Avg Time", justify="right", style="bold yellow") - table.add_column("Median Time", justify="right", style="bold yellow") - table.add_column("Stdev", justify="right", style="bold yellow") - - for benchmark in results.benchmark_results: - table.add_row( - benchmark.description, - str(benchmark.avg_time), - str(benchmark.median_time), - str(benchmark.std_dev), - ) - - console.print(table) + for benchmark_group_result in type_grouped_benchmarks: + console.print(report_generator.generate_benchmark_table_header(benchmark_group_result)) + console.print(report_generator.generate_benchmark_report_table(benchmark_group_result)) @router.command(Command("release-generate", description="Generate release report")) diff --git a/metrics/services/report_generator.py b/metrics/services/report_generator.py index e69de29..4765221 100644 --- a/metrics/services/report_generator.py +++ b/metrics/services/report_generator.py @@ -0,0 +1,73 @@ +from rich.panel import Panel +from rich.table import Table +from rich.text import Text + +from ..benchmarks.core.models import BenchmarkGroupResult +from metrics.services.system_info_reader import SystemInfo + + +class ReportGenerator: + def __init__(self, system_info: SystemInfo): + self.system_info = system_info + self._cached_benchmark_tables: dict[int, Table] = {} + self._cached_system_info_table: Table | None = None + + def generate_benchmark_report_table(self, benchmark_group_result: BenchmarkGroupResult) -> Table: + if cached_result := self._cached_benchmark_tables.get(id(benchmark_group_result)): + return cached_result + + table = Table(show_header=True, header_style="bold cyan", border_style="blue", show_lines=True) + table.add_column("Description", style="dim") + table.add_column("Avg Time", justify="right", style="bold yellow") + table.add_column("Median Time", justify="right", style="bold yellow") + table.add_column("Stdev", justify="right", style="bold yellow") + + for benchmark in benchmark_group_result.benchmark_results: + table.add_row( + benchmark.description, + str(benchmark.avg_time), + str(benchmark.median_time), + str(benchmark.std_dev), + ) + self._cached_benchmark_tables[id(benchmark_group_result)] = table + return table + + @staticmethod + def generate_benchmark_table_header(benchmark_group_result: BenchmarkGroupResult) -> Panel: + header_text = Text(f"TYPE: {benchmark_group_result.type_.upper()} ; " + f"ITERATIONS: {benchmark_group_result.iterations} ; " + f"ALL TIME IN MS", + style="bold magenta") + return Panel(header_text, expand=False, border_style="magenta") + + def generate_system_info_table(self) -> Table: + if self._cached_system_info_table is not None: + return self._cached_system_info_table + + table = Table(show_header=True, header_style="bold cyan", border_style="blue", show_lines=True) + table.add_column("Parameter", style="green") + table.add_column("Value", style="yellow") + + table.add_row("OS Name", self.system_info.os_info.name) + table.add_row("OS Kernel Version", self.system_info.os_info.kernel_version) + table.add_row("Architecture", self.system_info.cpu_info.architecture) + table.add_row("CPU", self.system_info.cpu_info.name) + table.add_row("CPU Physical Cores", str(self.system_info.cpu_info.physical_cores)) + table.add_row("CPU Logical Cores", str(self.system_info.cpu_info.logical_cores)) + table.add_row("CPU Max Frequency", str(self.system_info.cpu_info.max_frequency) + ' GHz') + table.add_row("CPU Min Frequency", str(self.system_info.cpu_info.min_frequency) + ' GHz') + table.add_row("CPU Current Frequency", str(self.system_info.cpu_info.current_frequency) + ' GHz') + table.add_row("Total RAM", str(self.system_info.memory_info.total_ram) + ' GB') + table.add_row("Used RAM", str(self.system_info.memory_info.used_ram) + ' GB') + table.add_row("Available RAM", str(self.system_info.memory_info.available_ram) + ' GB') + table.add_row("Python Version", self.system_info.python_info.version) + table.add_row("Python Implementation", self.system_info.python_info.implementation) + table.add_row("Python Compiler", self.system_info.python_info.compiler) + + self._cached_system_info_table = table + return table + + @staticmethod + def generate_system_info_header() -> Panel: + header_text = Text("SYSTEM INFO", style="bold magenta") + return Panel(header_text, expand=False, border_style="magenta") \ No newline at end of file diff --git a/metrics/services/system_info_reader.py b/metrics/services/system_info_reader.py index 47d17e6..2578158 100644 --- a/metrics/services/system_info_reader.py +++ b/metrics/services/system_info_reader.py @@ -1,13 +1,11 @@ __all__ = [ "SystemInfo", - "SystemInfoGetter", "get_system_info" ] from dataclasses import dataclass import platform import sys -from typing import Protocol import cpuinfo import psutil @@ -18,72 +16,46 @@ class SystemInfo: os_info: OSInfo cpu_info: CPUInfo memory_info: MemoryInfo - python_version: str - python_implementation: str - + python_info: PythonInfo @dataclass(frozen=True, slots=True) class OSInfo: - os_name: str + name: str kernel_version: str - @dataclass(frozen=True, slots=True) class CPUInfo: - cpu_name: str - cpu_architecture: str - cpu_physical_cores: int - cpu_logical_cores: int - cpu_max_frequency: float - cpu_base_frequency: float - + name: str + architecture: str + physical_cores: int + logical_cores: int + max_frequency: float + min_frequency: float + current_frequency: float @dataclass(frozen=True, slots=True) class MemoryInfo: total_ram: float # in GB + used_ram: float # in GB available_ram: float # in GB - l1_cache: float - l2_cache: float - l3_cache: float - @dataclass(frozen=True, slots=True) class PythonInfo: - python_version: str - python_implementation: str - python_compiler: str - - -class SystemInfoGetter(Protocol): - def __call__(self) -> SystemInfo: - raise NotImplementedError + version: str + implementation: str + compiler: str def get_system_info() -> SystemInfo: os_info = get_os_info() - os_name = os_info.os_name - os_kernel_version = os_info.kernel_version - - cpu_info = cpuinfo.get_cpu_info() - cpu_architecture = cpu_info["arch"] - cpu_name = cpu_info["brand_raw"] - - gpu_name = get_gpu_name() - - total_ram = psutil.virtual_memory().total / (1024 ** 3) - - python_version = platform.python_version() - python_implementation = platform.python_implementation() - + cpu_info = get_cpu_info() + memory_info = get_memory_info() + python_info = get_python_info() return SystemInfo( - os_name=os_name, - kernel_version=os_kernel_version, - cpu_architecture=cpu_architecture, - cpu_name=cpu_name, - gpu_name=gpu_name, - total_ram=total_ram, - python_version=python_version, - python_implementation=python_implementation, + os_info=os_info, + cpu_info=cpu_info, + memory_info=memory_info, + python_info=python_info, ) def get_os_info() -> OSInfo: @@ -99,16 +71,62 @@ def get_os_info() -> OSInfo: product_name = "Windows 10" return OSInfo( - os_name=product_name, + name=product_name, kernel_version=kernel_version, ) elif system == "Darwin": return OSInfo( kernel_version=platform.release(), - os_name=f"macOS {platform.mac_ver()[0]}" + name=f"macOS {platform.mac_ver()[0]}" ) else: return OSInfo( kernel_version=platform.release(), - os_name=platform.system() + name=platform.system() ) + +def get_cpu_info() -> CPUInfo: + cpu_info = cpuinfo.get_cpu_info() + cpu_name = cpu_info["brand_raw"] + cpu_architecture = cpu_info["arch"] + cpu_physical_cores = psutil.cpu_count(logical=False) + cpu_logical_cores = psutil.cpu_count(logical=True) + + cpu_freq = psutil.cpu_freq() or "N/A" + cpu_current_frequency = cpu_freq.current + cpu_min_frequency = cpu_freq.min + cpu_max_frequency = cpu_freq.max + + return CPUInfo( + name=cpu_name, + architecture=cpu_architecture, + physical_cores=cpu_physical_cores, + logical_cores=cpu_logical_cores, + current_frequency=cpu_current_frequency, + min_frequency=cpu_min_frequency, + max_frequency=cpu_max_frequency + ) + +def get_memory_info() -> MemoryInfo: + mem = psutil.virtual_memory() + total_ram = round(mem.total / (1024**3), 2) + used_ram = round(mem.used / (1024**3), 2) + available_ram = round(mem.available / (1024**3), 2) + + return MemoryInfo( + total_ram=total_ram, + used_ram=used_ram, + available_ram=available_ram, + ) + +def get_python_info() -> PythonInfo: + python_version = platform.python_version() + python_implementation = platform.python_implementation() + python_compiler = platform.python_compiler() + return PythonInfo( + version=python_version, + implementation=python_implementation, + compiler=python_compiler + ) + + diff --git a/mock/local_test.py b/mock/local_test.py index 9e20232..5af84a4 100644 --- a/mock/local_test.py +++ b/mock/local_test.py @@ -1,61 +1,6 @@ -import math +a = BrokenPipeError() +def q(f): + print(id(f)) - -def estimate_nth_prime_upper_bound(n: int): - if n < 6: - return 15 - - log_n = math.log(n) - log_log_n = math.log(log_n) - - if n < 100: - return int(n * (log_n + log_log_n) * 1.5) - elif n < 1000: - return int(n * (log_n + log_log_n) * 1.3) - elif n >= 8009824: - return int(n * (log_n + log_log_n - 1 + 1.8 * log_log_n / log_n)) - else: - return int(n * (log_n + log_log_n - 1 + 2.0 * log_log_n / log_n)) - - -def odd_dig_primes(n: int) -> list[int]: - nums = {k: True for k in range(2, n+1)} - - for num, is_checkable in nums.items(): - if not is_checkable: - continue - - if nums[2]: - nums[2] = False - - for x in range(num * num, n, num): - nums[x] = False - - primes = len([x for x in nums.items() if x[1]]) - max_prime = max([x[0] for x in nums.items() if x[1]]) - - upper_bound = estimate_nth_prime_upper_bound(primes+1) - print(upper_bound) - nums2 = {k: True for k in range(2, upper_bound)} - - for num, is_checkable in nums2.items(): - if not is_checkable: - continue - - if nums2[2]: - nums2[2] = False - - for x in range(num * num, upper_bound, num): - nums2[x] = False - - print([x for x in nums2.items() if x[1]]) - - next_prime_after_max = [x[0] for x in nums2.items() if x[1]][-1] - - return [ - primes, - max_prime, - next_prime_after_max - ] - -print(odd_dig_primes(13)) \ No newline at end of file +print(id(a)) +q(a) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index ed0413d..46c5294 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,14 +41,6 @@ metrics = [ ] [tool.ruff] -exclude = [ - ".idea", - "venv", - ".git", - "poetry.lock", - ".__pycache__", - "tests" -] line-length=90 [tool.pyright] @@ -79,5 +71,5 @@ disable_error_code = "import-untyped" line_length=90 [build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" +requires = ["uv_build"] +build-backend = "uv_build"