-
-
Notifications
You must be signed in to change notification settings - Fork 33.1k
gh-139424: microoptimize _collections_abc._check_methods #139401
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -106,11 +106,11 @@ async def _ag(): yield | |||||||||||||||||||||||
### ONE-TRICK PONIES ### | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
def _check_methods(C, *methods): | ||||||||||||||||||||||||
mro = C.__mro__ | ||||||||||||||||||||||||
mro_dicts = [B.__dict__ for B in C.__mro__] | ||||||||||||||||||||||||
for method in methods: | ||||||||||||||||||||||||
for B in mro: | ||||||||||||||||||||||||
if method in B.__dict__: | ||||||||||||||||||||||||
if B.__dict__[method] is None: | ||||||||||||||||||||||||
for base_dict in mro_dicts: | ||||||||||||||||||||||||
if method in base_dict: | ||||||||||||||||||||||||
if base_dict[method] is None: | ||||||||||||||||||||||||
return NotImplemented | ||||||||||||||||||||||||
break | ||||||||||||||||||||||||
Comment on lines
+111
to
115
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This avoids calculating hash and looking for key in dict twice. sentinel object can be created at the top of the method. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That looks like a clear win, good eye! Might make sense to do the check optimistically, ie: consider the sentinel more likely than the None if method_impl is sentinel:
continue
if method_impl is None:
return NotImplemented
break |
||||||||||||||||||||||||
else: | ||||||||||||||||||||||||
|
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.
Maybe we should use a tuple instead of a list to reduce memory usage, since this collection isn’t modified:
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.
would it be reasonable to cache this tuple for re-use?
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.
But the caching here would need to be more sophisticated, since when the class is updated, the result must also change. Standard caching only takes the input into account, so in this case the input would be cached, but the output should actually differ depending on the class 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.
can a class mro change after the fact?
I know the contents of dict could change but if the cache is just holding references to each class' dict itself, then any modifications of any dict should already be reflected in the cache
If the mro can change this becomes invalid
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.
to be clear I'm not suggesting to cache the result of this function, only the mro dict tuple
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.
Ah, got it. I think caching the MRO dict tuple should be safe.
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.
Here you also need to decide what exactly we want to optimize, since a tuple gives about ~10% better memory efficiency, while a list is about ~10% faster when iterating in loops.
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.
interesting, I didn't know the lists are faster to iterate thru, even with the length check at each iteration. TIL, thanks
In that case I'd opt for the list for top speed in the subclass check but I'm not sure how that fits into the best practices here or what the people want
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.
It's not worth debating tiny differences like this until we've verified that the change itself is worth pursing.