From 5390bc9f2fd7ff96fbe0dca827ee07dbebe475da Mon Sep 17 00:00:00 2001 From: Amethyst Reese Date: Tue, 27 Feb 2024 23:43:49 -0800 Subject: [PATCH 1/2] Simple benchmark of ufmt_file and ufmt_paths [ghstack-poisoned] --- makefile | 4 ++ ufmt/tests/benchmark.py | 90 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 ufmt/tests/benchmark.py diff --git a/makefile b/makefile index 38e4a35..a251699 100644 --- a/makefile +++ b/makefile @@ -37,6 +37,10 @@ test: deps: python -m pessimist -c 'python -m $(SRCS).tests' --requirements= --fast . +.PHONY: benchmark +benchmark: + python -m $(SRCS).tests.benchmark + .PHONY: html html: .venv README.md docs/*.rst docs/conf.py source $(ACTIVATE) && sphinx-build -ab html docs html diff --git a/ufmt/tests/benchmark.py b/ufmt/tests/benchmark.py new file mode 100644 index 0000000..9805028 --- /dev/null +++ b/ufmt/tests/benchmark.py @@ -0,0 +1,90 @@ +# Copyright Amethyst Reese, Tim Hatch +# Licensed under the MIT license + +import time +from pathlib import Path +from typing import Any, List + +from typing_extensions import Self + +from ufmt import ufmt_file, ufmt_paths + +ROOT = Path(__file__).parent.parent.parent + + +class Timer: + def __init__(self, name: str) -> None: + self.name = name + self.totals: List[int] = [] + + @classmethod + def fields(self) -> str: + headline = f"{'name':^40} {'min':^10} {'mean':^10} {'max':^10}" + underline = "-" * len(headline) + return f"{headline}\n{underline}" + + def __str__(self) -> str: + short = min(self.totals) + long = max(self.totals) + avg = sum(self.totals) // len(self.totals) + fields = " ".join(f"{value // 1000:>7} µs" for value in (short, avg, long)) + return f"{self.name + ':':<40} {fields}" + + def __enter__(self) -> Self: + self.before = time.monotonic_ns() + return self + + def __exit__(self, *args: Any) -> None: + after = time.monotonic_ns() + self.totals.append(after - self.before) + + +def benchmark() -> None: + print("starting benchmark...") + + ufmt_dir = ROOT / "ufmt" + ufmt_core = ufmt_dir / "core.py" + assert ufmt_dir.is_dir(), f"{ufmt_dir} not found, must run benchmark from repo" + + print() + print(Timer.fields()) + + timer = Timer("ufmt_file") + for _ in range(5): + with timer: + ufmt_file(ufmt_core, dry_run=True) + print(timer) + + timer = Timer("ufmt_file, diff=True") + for _ in range(5): + with timer: + ufmt_file(ufmt_core, dry_run=True, diff=True) + print(timer) + + timer = Timer("ufmt_file, return_content=True") + for _ in range(5): + with timer: + ufmt_file(ufmt_core, dry_run=True, return_content=True) + print(timer) + + timer = Timer("ufmt_paths") + for _ in range(5): + with timer: + list(ufmt_paths([ufmt_dir], dry_run=True)) + print(timer) + + timer = Timer("ufmt_paths, diff=True") + for _ in range(5): + with timer: + list(ufmt_paths([ufmt_dir], dry_run=True, diff=True)) + print(timer) + + timer = Timer("ufmt_paths, return_content=True") + for _ in range(5): + with timer: + list(ufmt_paths([ufmt_dir], dry_run=True, diff=True)) + print(timer) + + +if __name__ == "__main__": + benchmark() From 1a5793d5328a831f5fb5553b903dc6cc79a59025 Mon Sep 17 00:00:00 2001 From: Amethyst Reese Date: Wed, 28 Feb 2024 00:17:16 -0800 Subject: [PATCH 2/2] Update on "Simple benchmark of ufmt_file and ufmt_paths" [ghstack-poisoned] --- ufmt/tests/benchmark.py | 89 ++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/ufmt/tests/benchmark.py b/ufmt/tests/benchmark.py index 9805028..41f1dc6 100644 --- a/ufmt/tests/benchmark.py +++ b/ufmt/tests/benchmark.py @@ -12,78 +12,69 @@ ROOT = Path(__file__).parent.parent.parent -class Timer: - def __init__(self, name: str) -> None: - self.name = name +class Benchmark: + def __init__(self, target: int = 5) -> None: + self.target = target + self.last_time: int = 0 + self.title: str = "" self.totals: List[int] = [] @classmethod def fields(self) -> str: - headline = f"{'name':^40} {'min':^10} {'mean':^10} {'max':^10}" + headline = f"{'benchmark':^40} {'min':^10} {'mean':^10} {'max':^10}" underline = "-" * len(headline) return f"{headline}\n{underline}" - def __str__(self) -> str: - short = min(self.totals) - long = max(self.totals) - avg = sum(self.totals) // len(self.totals) + def results(self) -> str: + totals = self.totals + short = min(totals) + long = max(totals) + avg = sum(totals) // len(totals) fields = " ".join(f"{value // 1000:>7} µs" for value in (short, avg, long)) - return f"{self.name + ':':<40} {fields}" + return f"{self.title + ':':<40} {fields}" def __enter__(self) -> Self: - self.before = time.monotonic_ns() + print() + print(self.fields()) return self def __exit__(self, *args: Any) -> None: - after = time.monotonic_ns() - self.totals.append(after - self.before) + print() + def __call__(self, name: str) -> Self: + self.title = name + return self -def benchmark() -> None: - print("starting benchmark...") + def __iter__(self) -> Self: + self.last_time = 0 + self.totals = [] + return self + + def __next__(self) -> None: + now = time.monotonic_ns() + if self.last_time > 0: + self.totals += [now - self.last_time] + if len(self.totals) >= self.target: + print(self.results()) + self.totals = [] + self.title = "" + raise StopIteration + self.last_time = time.monotonic_ns() + + +def benchmark() -> None: ufmt_dir = ROOT / "ufmt" ufmt_core = ufmt_dir / "core.py" assert ufmt_dir.is_dir(), f"{ufmt_dir} not found, must run benchmark from repo" - print() - print(Timer.fields()) + with Benchmark() as benchmark: - timer = Timer("ufmt_file") - for _ in range(5): - with timer: + for _ in benchmark("ufmt_file"): ufmt_file(ufmt_core, dry_run=True) - print(timer) - - timer = Timer("ufmt_file, diff=True") - for _ in range(5): - with timer: - ufmt_file(ufmt_core, dry_run=True, diff=True) - print(timer) - - timer = Timer("ufmt_file, return_content=True") - for _ in range(5): - with timer: - ufmt_file(ufmt_core, dry_run=True, return_content=True) - print(timer) - - timer = Timer("ufmt_paths") - for _ in range(5): - with timer: + + for _ in benchmark("ufmt_paths"): list(ufmt_paths([ufmt_dir], dry_run=True)) - print(timer) - - timer = Timer("ufmt_paths, diff=True") - for _ in range(5): - with timer: - list(ufmt_paths([ufmt_dir], dry_run=True, diff=True)) - print(timer) - - timer = Timer("ufmt_paths, return_content=True") - for _ in range(5): - with timer: - list(ufmt_paths([ufmt_dir], dry_run=True, diff=True)) - print(timer) if __name__ == "__main__":