bbbbbbenchh

This commit is contained in:
2026-01-17 23:43:59 +03:00
parent 2e5b19f4d8
commit 1648a8206a
6 changed files with 153 additions and 92 deletions
+14
View File
@@ -0,0 +1,14 @@
class BenchmarkNotFound(Exception):
def __init__(self, benchmark_name: str):
self.benchmark_name = benchmark_name
def __str__(self):
return f"Benchmark with name '{self.benchmark_name}' not found"
class BenchmarksNotFound(Exception):
def __init__(self, type_: str):
self.type_ = type_
def __str__(self):
return f"Benchmarks with type '{self.type_}' not found"
+101 -62
View File
@@ -1,26 +1,39 @@
__all__ = [ __all__ = [
"Benchmark", "Benchmark",
"Benchmarks", "Benchmarks",
"BenchmarkResult", "BenchmarkResult"
"benchmark"
] ]
from dataclasses import dataclass from dataclasses import dataclass
from decimal import Decimal import time
from typing import Callable, ClassVar, overload, override import gc
import statistics
from typing import Callable, override
from .exceptions import BenchmarkNotFound, BenchmarksNotFound
BenchmarkAsFunc = Callable[[], float] BenchmarkAsFunc = Callable[[], float]
@dataclass(frozen=True) @dataclass(frozen=True, slots=True)
class BenchmarkResult: class BenchmarkResult:
type_: str type_: str
name: str name: str
description: str description: str
iterations: int iterations: int
avg_time: Decimal is_gc_disabled: bool
avg_time: float
median_time: float
std_dev: float
@dataclass(frozen=True, slots=True)
class BenchmarkGroupResult:
type_: str
benchmark_results: list[BenchmarkResult]
class Benchmark: class Benchmark:
def __init__( def __init__(
self, self,
@@ -28,83 +41,109 @@ class Benchmark:
*, *,
type_: str, type_: str,
name: str, name: str,
description: str, description: str
iterations: int
) -> None: ) -> None:
self.func = func self.func = func
self.type_ = type_ self.type_ = type_
self.name = name self.name = name
self.description = description self.description = description
self.iterations = iterations
def run(self) -> float: def single_run(self, is_gc_disabled: bool = False) -> float:
return self.func() if is_gc_disabled:
was_gc_enabled = gc.isenabled()
gc.disable()
start = time.perf_counter()
self.func()
end = time.perf_counter()
if was_gc_enabled:
gc.enable()
gc.collect()
return end - start
else:
start = time.perf_counter()
self.func()
end = time.perf_counter()
return end - start
def multiple_runs(self, iterations: int, is_gc_disabled: bool = False) -> tuple[float]:
run_attempts: list[float] = []
for _ in range(iterations):
run_attempts.append(self.single_run(is_gc_disabled))
return tuple(*run_attempts)
@override @override
def __repr__(self) -> str: def __repr__(self) -> str:
return f'Benchmark<{self.type_=}, {self.name=}, {self.description=}, {self.iterations=}>' return f'Benchmark<{self.type_=}, {self.name=}, {self.description=}>'
@override @override
def __str__(self) -> str: def __str__(self) -> str:
return f'Benchmark({self.type_=}, {self.name=}, {self.description=}, {self.iterations=})' return f'benchmark {self.name} with type {self.type_}'
class Benchmarks: class Benchmarks:
_benchmarks: ClassVar[list[Benchmark]] = [] def __init__(self, *benchmarks: Benchmark) -> None:
self._benchmarks: list[Benchmark] = list(benchmarks)
self._benchmarks_grouped_by_type: dict[str, list[Benchmark]] = {}
self._benchmarks_paired_by_name: dict[str, Benchmark] = {}
@overload
@classmethod
def register( def register(
cls, self,
call: BenchmarkAsFunc, type_: str,
*, description: str = ""
type_: str = "",
description: str = "",
iterations: int = 100,
) -> BenchmarkAsFunc:
...
@overload
@classmethod
def register(
cls,
call: None = None,
*,
type_: str = "",
description: str = "",
iterations: int = 100,
) -> Callable[[BenchmarkAsFunc], BenchmarkAsFunc]: ) -> Callable[[BenchmarkAsFunc], BenchmarkAsFunc]:
...
@classmethod
def register(
cls,
call: BenchmarkAsFunc | None = None,
*,
type_: str = "",
description: str = "",
iterations: int = 100,
) -> Callable[[BenchmarkAsFunc], BenchmarkAsFunc] | BenchmarkAsFunc:
def decorator(func: BenchmarkAsFunc) -> BenchmarkAsFunc: def decorator(func: BenchmarkAsFunc) -> BenchmarkAsFunc:
cls._benchmarks.append( benchmark = Benchmark(
Benchmark( func,
func, type_=type_,
type_=type_, name=func.__name__,
name=func.__name__, description=description or f'description for {func.__name__} with type {type_}',
description=description or f'description for {func.__name__} with {iterations} iterations',
iterations=iterations
)
) )
self._benchmarks.append(benchmark)
self._benchmarks_paired_by_name[type_] = benchmark
self._benchmarks_grouped_by_type.setdefault(type_, []).append(benchmark)
return func return func
return decorator
if call is None: def run_benchmark_by_name(self, name: str, iterations: int = 100, is_gc_disables: bool = False) -> BenchmarkResult:
return decorator benchmark = self.get_benchmark_by_name(name)
else: if not benchmark:
return decorator(call) raise BenchmarkNotFound(name)
run_attempts: tuple[float] = benchmark.multiple_runs(iterations, is_gc_disables)
@classmethod avg = statistics.mean(run_attempts)
def get_benchmarks(cls) -> list[Benchmark]: median = statistics.median(run_attempts)
return cls._benchmarks std_dev = statistics.stdev(run_attempts) if len(run_attempts) > 1 else 0
return BenchmarkResult(
type_=benchmark.type_,
name=benchmark.name,
description=benchmark.description,
iterations=iterations,
is_gc_disabled=is_gc_disables,
avg_time=avg,
median_time=median,
std_dev=std_dev
)
benchmark = Benchmarks.register def run_benchmarks_by_type(self, type_: str, iterations: int = 100, is_gc_disabled: bool = False) -> BenchmarkGroupResult:
benchmarks = self.get_benchmarks_by_type(type_)
if not benchmarks:
raise BenchmarksNotFound(type_)
benchmark_results: list[BenchmarkResult] = []
for benchmark in benchmarks:
benchmark_results.append(self.run_benchmark_by_name(benchmark.name, iterations, is_gc_disabled))
return BenchmarkGroupResult(
type_=type_,
benchmark_results=benchmark_results
)
def get_benchmarks_by_type(self, type_: str) -> list[Benchmark]:
return self._benchmarks_grouped_by_type.get(type_, [])
def get_benchmark_by_name(self, name: str) -> Benchmark | None:
return self._benchmarks_paired_by_name.get(name)
+19 -27
View File
@@ -3,10 +3,8 @@ __all__ = [
"get_time_of_validate_routers_for_collisions", "get_time_of_validate_routers_for_collisions",
"get_time_of_most_similar_command", "get_time_of_most_similar_command",
"get_time_of_finds_appropriate_handler", "get_time_of_finds_appropriate_handler",
"attempts_to_average", "get_kernel_version",
"run_benchmark", "get_gpu_info"
"run_all_benchmarks",
"get_kernel_version"
] ]
import io import io
@@ -18,6 +16,8 @@ from concurrent.futures import ProcessPoolExecutor
from contextlib import redirect_stdout from contextlib import redirect_stdout
from decimal import ROUND_HALF_UP, Decimal from decimal import ROUND_HALF_UP, Decimal
import pynvml
from argenta import App from argenta import App
from argenta.router import Router from argenta.router import Router
from argenta.command.models import InputCommand from argenta.command.models import InputCommand
@@ -48,7 +48,7 @@ def get_time_of_most_similar_command(app: App, unknown_command: str) -> float:
return (end - start) * 1000 return (end - start) * 1000
def get_time_of_finds_appropriate_handler(router: "Router", input_command: "InputCommand") -> float: def get_time_of_finds_appropriate_handler(router: Router, input_command: InputCommand) -> float:
start = time.perf_counter() start = time.perf_counter()
with redirect_stdout(io.StringIO()): with redirect_stdout(io.StringIO()):
router.finds_appropriate_handler(input_command) router.finds_appropriate_handler(input_command)
@@ -90,29 +90,21 @@ def get_kernel_version() -> dict[str, str]:
'product_name': platform.system(), 'product_name': platform.system(),
} }
def get_gpu_info() -> str:
try:
pynvml.nvmlInit()
device_count = pynvml.nvmlDeviceGetCount()
if device_count == 0:
return "N/A"
def attempts_to_average(bench_attempts: list[float], iterations: int) -> Decimal: handle = pynvml.nvmlDeviceGetHandleByIndex(0)
return Decimal(sum(bench_attempts) / iterations).quantize(Decimal("0.0001"), rounding=ROUND_HALF_UP) name = pynvml.nvmlDeviceGetName(handle)
if isinstance(name, bytes):
name = name.decode("utf-8")
def run_all_benchmarks() -> dict[str, list[BenchmarkResult]]: pynvml.nvmlShutdown()
all_benchmarks: list[Benchmark] = Benchmarks.get_benchmarks() return name
except pynvml.NVMLError:
return "N/A"
workers = os.cpu_count() or 1
with ProcessPoolExecutor(max_workers=workers) as executor:
results = executor.map(run_benchmark, all_benchmarks)
type_paired_benchmarks: dict[str, list[BenchmarkResult]] = {}
for result in results:
type_paired_benchmarks.setdefault(result.type_, []).append(result)
return type_paired_benchmarks
def run_benchmark(benchmark: Benchmark) -> BenchmarkResult:
bench_attempts: list[float] = []
for _ in range(benchmark.iterations):
bench_attempts.append(benchmark.run())
avg = attempts_to_average(bench_attempts, benchmark.iterations)
return BenchmarkResult(benchmark.type_, benchmark.name, benchmark.description, benchmark.iterations, avg)
+4 -2
View File
@@ -10,7 +10,7 @@ from argenta.command.models import Command
from argenta.response import Response from argenta.response import Response
from argenta.router import Router from argenta.router import Router
from .benchmarks.models import BenchmarkResult from .benchmarks.models import BenchmarkResult
from .benchmarks.utils import run_all_benchmarks, get_kernel_version from .benchmarks.utils import run_all_benchmarks, get_kernel_version, get_gpu_info
console = Console() console = Console()
router = Router(title="Metrics commands:") router = Router(title="Metrics commands:")
@@ -19,6 +19,7 @@ router = Router(title="Metrics commands:")
@router.command(Command("all-print", description="Print all benchmarks results")) @router.command(Command("all-print", description="Print all benchmarks results"))
def all_print_handler(_: Response) -> None: def all_print_handler(_: Response) -> None:
cpu_info = cpuinfo.get_cpu_info() cpu_info = cpuinfo.get_cpu_info()
gpu_info = get_gpu_info()
os_info = get_kernel_version() os_info = get_kernel_version()
table = Table(show_header=True, header_style="bold cyan", border_style="blue", show_lines=True) table = Table(show_header=True, header_style="bold cyan", border_style="blue", show_lines=True)
@@ -29,7 +30,8 @@ def all_print_handler(_: Response) -> None:
table.add_row("OS Name", os_info['product_name']) table.add_row("OS Name", os_info['product_name'])
table.add_row("OS Kernel Version", os_info['kernel_version']) table.add_row("OS Kernel Version", os_info['kernel_version'])
table.add_row("Architecture", cpu_info['arch']) table.add_row("Architecture", cpu_info['arch'])
table.add_row("Processor", cpu_info['brand_raw']) 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 Version", cpu_info['python_version'])
table.add_row("Python Implementation", platform.python_implementation()) table.add_row("Python Implementation", platform.python_implementation())
+1
View File
@@ -36,6 +36,7 @@ tests = [
"pytest-mock>=3.15.1", "pytest-mock>=3.15.1",
] ]
metrics = [ metrics = [
"nvidia-ml-py>=13.590.44",
"py-cpuinfo>=9.0.0", "py-cpuinfo>=9.0.0",
] ]
Generated
+14 -1
View File
@@ -62,6 +62,7 @@ linters = [
{ name = "wemake-python-styleguide" }, { name = "wemake-python-styleguide" },
] ]
metrics = [ metrics = [
{ name = "nvidia-ml-py" },
{ name = "py-cpuinfo" }, { name = "py-cpuinfo" },
] ]
tests = [ tests = [
@@ -95,7 +96,10 @@ linters = [
{ name = "ruff", specifier = ">=0.12.12" }, { name = "ruff", specifier = ">=0.12.12" },
{ name = "wemake-python-styleguide", specifier = ">=0.17.0" }, { name = "wemake-python-styleguide", specifier = ">=0.17.0" },
] ]
metrics = [{ name = "py-cpuinfo", specifier = ">=9.0.0" }] metrics = [
{ name = "nvidia-ml-py", specifier = ">=13.590.44" },
{ name = "py-cpuinfo", specifier = ">=9.0.0" },
]
tests = [ tests = [
{ name = "pyfakefs", specifier = ">=5.5.0" }, { name = "pyfakefs", specifier = ">=5.5.0" },
{ name = "pytest", specifier = ">=8.3.2" }, { name = "pytest", specifier = ">=8.3.2" },
@@ -557,6 +561,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
] ]
[[package]]
name = "nvidia-ml-py"
version = "13.590.44"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1b/23/3871537f204aee823c574ba25cbeb08cae779979d4d43c01adddda00bab9/nvidia_ml_py-13.590.44.tar.gz", hash = "sha256:b358c7614b0fdeea4b95f046f1c90123bfe25d148ab93bb1c00248b834703373", size = 49737, upload-time = "2025-12-08T14:41:10.872Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/47/4c822bd37a008e72fd5a0eae33524ae3ac97b13f7030f63bae1728b8957e/nvidia_ml_py-13.590.44-py3-none-any.whl", hash = "sha256:18feb54eca7d0e3cdc8d1a040a771eda72d9ec3148e5443087970dbfd7377ecc", size = 50683, upload-time = "2025-12-08T14:41:09.597Z" },
]
[[package]] [[package]]
name = "packaging" name = "packaging"
version = "25.0" version = "25.0"