-
-
Notifications
You must be signed in to change notification settings - Fork 30.5k
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
gh-74690: typing: Simplify and optimise _ProtocolMeta.__instancecheck__
#103159
gh-74690: typing: Simplify and optimise _ProtocolMeta.__instancecheck__
#103159
Conversation
I’m still on vacation, and when I get back I will be overwhelmed by catching up, so it may take a while. |
The GitHub machinery automatically requested your review because you're a CODEOWNER. I think there's more than enough eyes on this; feel free to remove your request for review if you're on vacation! :) |
Ah. then I’m beginning to get annoyed by CODEOWNERS review requests in general. Maybe I should remove myself. I want to be notified but not requested to review. |
There's no good mechanism on GitHub to be automatically subscribed to PRs but not be auto-requested for review, unfortunately :( Though you can always manually subscribe to a PR thread. You're already subscribed to this thread, so I'll go ahead and remove your request for review on this for now :) |
Hope you're having a great vacation! |
This is the sort of tradeoff that is hard to evaluate without checking the performance impact on some realistic code bases using runtime-checkable Protocols. I have no idea how common it is in real code to explicitly subclass or register-as-implementing a Protocol, or why anyone would bother. I guess if we implement this PR, then that would be available as a performance optimization? It makes intuitive sense to do this, and I would probably lean towards doing it if the impact on performance of the non-explicit case is small; like <1-2%? (Sorry, don't have time right now to actually run benchmarks.) |
Looks like it would be a performance degradation somewhere in the range of between 5-10% rather than 1-2% for the non-explicit cases :/ I'm hesitant to be any more precise; as I say, the numbers on my machine seem pretty noisy for some reason |
Yeah, if we do this, then nominal (Not quite sure why |
The performance hit for the non-explicit cases has gone down to somewhere in the range of between 3-8% now that #103034 has been merged (because both |
I suddenly realised that, by moving the This PR now removes that logic and, as a result, all kinds of Benchmark results on
And with this PR:
|
_ProtocolMeta.__instancecheck__
_ProtocolMeta.__instancecheck__
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.
all kinds of isinstance() checks are now faster
Well, that does make it easier to evaluate the tradeoff ;)
It's odd to me that the function was written this way in the first place. It seems like someone took some care to ensure that being a true subclass was only sufficient if not a protocol class or if callable-members-only. But it also seems clear that this doesn't change semantics; there's no other return False
in the function, so there's no way in which moving super().__instancecheck__(instance)
higher up in the function can cause to return True
for true subclasses when we wouldn't have before.
This comment was marked as off-topic.
This comment was marked as off-topic.
It's possible that the way it was written used to provide an optimisation. The
All of which is to say: I definitely wouldn't be surprised if delaying the |
This implements the optimisation suggested by @ilevkivskyi in #74690 (comment). However, I'm not sure if it's the right thing to do. I'm posting it here for discussion, and so that people can have a play around with it.
This dramatically speeds up
isinstance()
checks for nominal subclasses of runtime-checkable protocols and subclasses registered viaABCMeta.register
, i.e. situations like the following:However, speeding these two up comes at the cost of the performance of
isinstance()
checks with "structural subclasses". These are all slightly slower:So the question is: is it worth making
isinstance()
calls against nominal and registered subclasses much, much faster, at the cost of making those^isinstance()
calls a bit slower? I'm not sure it is, as kind of the "whole point" of runtime-checkable protocols is that an objectx
doesn't have to directly inherit from a protocolX
in order it to be considered an instance ofX
. So I don't know how common it is for people to directly inherit from protocols in their own code.But, I'm very curious about what other people think.
Here's a benchmarking script for people to play around with. The results of the benchmark script for the structural-subclass cases are a bit noisy on my machine, but consistently a little bit slower than on
main
:Benchmark script