From 9e7605309387ce495ed803f52f3ade818b4a2d2b Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Tue, 14 Feb 2023 18:09:36 -0800 Subject: [PATCH 1/2] add comprehensions benchmark --- pyperformance/data-files/benchmarks/MANIFEST | 1 + .../bm_comprehensions/pyproject.toml | 9 ++ .../bm_comprehensions/run_benchmark.py | 95 +++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 pyperformance/data-files/benchmarks/bm_comprehensions/pyproject.toml create mode 100644 pyperformance/data-files/benchmarks/bm_comprehensions/run_benchmark.py diff --git a/pyperformance/data-files/benchmarks/MANIFEST b/pyperformance/data-files/benchmarks/MANIFEST index a851218b..728bd984 100644 --- a/pyperformance/data-files/benchmarks/MANIFEST +++ b/pyperformance/data-files/benchmarks/MANIFEST @@ -17,6 +17,7 @@ gc_collect generators chameleon chaos +comprehensions crypto_pyaes dask deepcopy diff --git a/pyperformance/data-files/benchmarks/bm_comprehensions/pyproject.toml b/pyperformance/data-files/benchmarks/bm_comprehensions/pyproject.toml new file mode 100644 index 00000000..aef342bb --- /dev/null +++ b/pyperformance/data-files/benchmarks/bm_comprehensions/pyproject.toml @@ -0,0 +1,9 @@ +[project] +name = "pyperformance_bm_comprehensions" +requires-python = ">=3.10" +dependencies = ["pyperf"] +urls = {repository = "https://github.com/python/pyperformance"} +dynamic = ["version"] + +[tool.pyperformance] +name = "comprehensions" diff --git a/pyperformance/data-files/benchmarks/bm_comprehensions/run_benchmark.py b/pyperformance/data-files/benchmarks/bm_comprehensions/run_benchmark.py new file mode 100644 index 00000000..bb275c99 --- /dev/null +++ b/pyperformance/data-files/benchmarks/bm_comprehensions/run_benchmark.py @@ -0,0 +1,95 @@ +""" +Benchmark comprehensions. + +Author: Carl Meyer +""" + +from dataclasses import dataclass +from enum import Enum +from typing import Iterable + +import pyperf + + +class WidgetKind(Enum): + BIG = 1 + SMALL = 2 + + +@dataclass +class Widget: + widget_id: int + creator_id: int + derived_widget_ids: list[int] + kind: WidgetKind + has_knob: bool + has_spinner: bool + + +class WidgetTray: + def __init__(self, owner_id: int, widgets: list[Widget]) -> None: + self.owner_id = owner_id + self.sorted_widgets: list[Widget] = [] + self._add_widgets(widgets) + + def _any_knobby(self, widgets: Iterable[Widget | None]) -> bool: + return any(w.has_knob for w in widgets if w) + + def _is_big_spinny(self, widget: Widget) -> bool: + return widget.kind == WidgetKind.BIG and widget.has_spinner + + def _add_widgets(self, widgets: list[Widget]) -> None: + # sort order: mine first, then any widgets with derived knobby widgets in order of + # number derived, then other widgets in order of number derived, and we exclude + # big spinny widgets entirely + widgets = [w for w in widgets if not self._is_big_spinny(w)] + id_to_widget = {w.widget_id: w for w in widgets} + id_to_derived = { + w.widget_id: [id_to_widget.get(dwid) for dwid in w.derived_widget_ids] + for w in widgets + } + sortable_widgets = [ + ( + w.creator_id == self.owner_id, + self._any_knobby(id_to_derived[w.widget_id]), + len(id_to_derived[w.widget_id]), + w.widget_id, + ) + for w in widgets + ] + sortable_widgets.sort() + self.sorted_widgets = [id_to_widget[sw[-1]] for sw in sortable_widgets] + + +def make_some_widgets() -> list[Widget]: + widget_id = 0 + widgets = [] + for creator_id in range(3): + for kind in WidgetKind: + for has_knob in [True, False]: + for has_spinner in [True, False]: + derived = [w.widget_id for w in widgets[::creator_id + 1]] + widgets.append( + Widget( + widget_id, creator_id, derived, kind, has_knob, has_spinner + ) + ) + widget_id += 1 + assert len(widgets) == 24 + return widgets + + +def bench_comprehensions(loops: int) -> float: + range_it = range(loops) + widgets = make_some_widgets() + t0 = pyperf.perf_counter() + for _ in range_it: + tray = WidgetTray(1, widgets) + assert len(tray.sorted_widgets) == 18 + return pyperf.perf_counter() - t0 + + +if __name__ == "__main__": + runner = pyperf.Runner() + runner.metadata["description"] = "Benchmark comprehensions" + runner.bench_time_func("comprehensions", bench_comprehensions) From 58bcf182ca7cf84bc4ac5a1e3f69e2b55831e443 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Wed, 15 Feb 2023 08:53:24 -0800 Subject: [PATCH 2/2] make annotations 3.7-compatible --- .../benchmarks/bm_comprehensions/pyproject.toml | 2 +- .../benchmarks/bm_comprehensions/run_benchmark.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pyperformance/data-files/benchmarks/bm_comprehensions/pyproject.toml b/pyperformance/data-files/benchmarks/bm_comprehensions/pyproject.toml index aef342bb..4f47b6d2 100644 --- a/pyperformance/data-files/benchmarks/bm_comprehensions/pyproject.toml +++ b/pyperformance/data-files/benchmarks/bm_comprehensions/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pyperformance_bm_comprehensions" -requires-python = ">=3.10" +requires-python = ">=3.7" dependencies = ["pyperf"] urls = {repository = "https://github.com/python/pyperformance"} dynamic = ["version"] diff --git a/pyperformance/data-files/benchmarks/bm_comprehensions/run_benchmark.py b/pyperformance/data-files/benchmarks/bm_comprehensions/run_benchmark.py index bb275c99..0315820b 100644 --- a/pyperformance/data-files/benchmarks/bm_comprehensions/run_benchmark.py +++ b/pyperformance/data-files/benchmarks/bm_comprehensions/run_benchmark.py @@ -6,7 +6,7 @@ from dataclasses import dataclass from enum import Enum -from typing import Iterable +from typing import Iterable, List, Optional import pyperf @@ -20,25 +20,25 @@ class WidgetKind(Enum): class Widget: widget_id: int creator_id: int - derived_widget_ids: list[int] + derived_widget_ids: List[int] kind: WidgetKind has_knob: bool has_spinner: bool class WidgetTray: - def __init__(self, owner_id: int, widgets: list[Widget]) -> None: + def __init__(self, owner_id: int, widgets: List[Widget]) -> None: self.owner_id = owner_id - self.sorted_widgets: list[Widget] = [] + self.sorted_widgets: List[Widget] = [] self._add_widgets(widgets) - def _any_knobby(self, widgets: Iterable[Widget | None]) -> bool: + def _any_knobby(self, widgets: Iterable[Optional[Widget]]) -> bool: return any(w.has_knob for w in widgets if w) def _is_big_spinny(self, widget: Widget) -> bool: return widget.kind == WidgetKind.BIG and widget.has_spinner - def _add_widgets(self, widgets: list[Widget]) -> None: + def _add_widgets(self, widgets: List[Widget]) -> None: # sort order: mine first, then any widgets with derived knobby widgets in order of # number derived, then other widgets in order of number derived, and we exclude # big spinny widgets entirely @@ -61,7 +61,7 @@ def _add_widgets(self, widgets: list[Widget]) -> None: self.sorted_widgets = [id_to_widget[sw[-1]] for sw in sortable_widgets] -def make_some_widgets() -> list[Widget]: +def make_some_widgets() -> List[Widget]: widget_id = 0 widgets = [] for creator_id in range(3):