From 246037fb4ff0858471ed6a359852c9b8d2fe2abe Mon Sep 17 00:00:00 2001 From: Fernando Macedo Date: Sat, 7 Mar 2026 09:09:00 -0300 Subject: [PATCH 01/13] perf: cache State.__hash__ to avoid repeated repr() calls State.__hash__ was computing hash(repr(self)) on every call, generating a ~120-char f-string each time. Since State identity (name + id) is immutable after _set_id(), the hash is now precomputed once and cached. InstanceState.__hash__ also cached at construction time, eliminating both the weakref dereference and the repr() call. Also expands test_profiling.py with 14 benchmarks covering v3 features (compound, parallel, guards, history, deep history) and adds a benchmark workflow guide in tests/benchmarks/README.md. Measured impact vs baseline (pytest-benchmark, pedantic mode): - parallel_region_events: -11% - compound_enter_exit: -7% - history_pause_resume: -9% - deep_history_cycle: -6% - flat_self_transition: -1% --- statemachine/state.py | 7 +- tests/benchmarks/README.md | 163 +++++++++++++++++++++++++ tests/test_profiling.py | 238 ++++++++++++++++++++++++++++++++++++- 3 files changed, 401 insertions(+), 7 deletions(-) create mode 100644 tests/benchmarks/README.md diff --git a/statemachine/state.py b/statemachine/state.py index 32c436ff..34ee4cc3 100644 --- a/statemachine/state.py +++ b/statemachine/state.py @@ -246,6 +246,7 @@ def __init__( raise InvalidDefinition(_("'donedata' can only be specified on final states.")) self.enter.add(donedata, priority=CallbackPriority.INLINE) self.document_order = 0 + self._hash = id(self) self._init_states() def _init_states(self): @@ -267,7 +268,7 @@ def __eq__(self, other): ) def __hash__(self): - return hash(repr(self)) + return self._hash def _setup(self): self.enter.add("on_enter_state", priority=CallbackPriority.GENERIC, is_convention=True) @@ -320,6 +321,7 @@ def _set_id(self, id: str) -> "State": self.value = id if not self.name: self.name = self._id.replace("_", " ").capitalize() + self._hash = hash((self.name, self._id)) return self @@ -375,6 +377,7 @@ def __init__( ): self._state = ref(state) self._machine = ref(machine) + self._hash = hash(state) self._init_states() def _ref(self) -> State: @@ -411,7 +414,7 @@ def __eq__(self, other): return self._ref() == other def __hash__(self): - return hash(repr(self._ref())) + return self._hash def __repr__(self): return repr(self._ref()) diff --git a/tests/benchmarks/README.md b/tests/benchmarks/README.md new file mode 100644 index 00000000..de86d62b --- /dev/null +++ b/tests/benchmarks/README.md @@ -0,0 +1,163 @@ +# Performance Benchmarks + +Structured workflow for measuring and documenting optimization impact. + +## Quick reference + +```bash +# Run benchmarks and save a named snapshot +uv run pytest tests/test_profiling.py -m slow \ + --benchmark-only --benchmark-disable-gc \ + --benchmark-save=