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__
carljm
left a comment
There was a problem hiding this comment.
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 objectxdoesn't have to directly inherit from a protocolXin 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