mirror of
https://github.com/koloideal/Argenta.git
synced 2026-06-10 10:05:28 +03:00
bbbbbbenchh
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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())
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user