New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: Improve setup performance #401
Conversation
Codecov ReportAll modified lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## develop #401 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 20 20
Lines 995 1101 +106
Branches 165 175 +10
==========================================
+ Hits 995 1101 +106
Flags with carried forward coverage won't be shown. Click here to find out more.
☔ View full report in Codecov by Sentry. |
…StateMachine creation
…ch_callable to avoid the costs of duplicated register attempts
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
default_resolver = resolver_factory(machine, model) | ||
|
||
# clone states and transitions to avoid sharing callbacks references between instances | ||
self.states = States( | ||
{s.id: s.clone()._setup(self, default_resolver) for s in class_states} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the main change on this PR: Instead of cloning states to get isolated SM instances, we now register the callbacks references on the SM instance. So States and Transitions are kind of immutable at SM runtime.
@@ -70,6 +71,8 @@ def __init__( | |||
self.__rtc = rtc | |||
self.__processing: bool = False | |||
self._external_queue: deque = deque() | |||
self._callbacks_registry = CallbacksRegistry() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new _callbacks_registry
keeps the reference for all callback methods used in actions, conditions, and guards.
from .exceptions import AttrNotFound | ||
from .exceptions import InvalidDefinition | ||
from .i18n import _ | ||
from .utils import ensure_iterable | ||
|
||
|
||
class CallbackWrapper: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CallbackWrapper
is now only about concrete callback references, and all the metadata attributes are decomposed into a new CallbackMeta
.
machine.__dict__[self._storage] = self | ||
def _setup(self, register): | ||
register(self.enter) | ||
register(self.exit) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now like a "visitor" pattern.
@@ -208,6 +213,59 @@ def initial(self): | |||
def final(self): | |||
return self._final | |||
|
|||
|
|||
class InstanceState(State): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A thin "proxy" for State
that has is_active
with a reference to a concrete instance of the SM.
|
||
after("after_transition") | ||
def _setup(self, register): | ||
register(self.validators) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now like a visitor
pattern.
Related:
This is the initial profiling for reference:
Benchmark with
develop
Benchmark the current iteration: Setup 20% faster than
develop
.To reproduce, run: