gh-92810: Reduce memory usage by ABCMeta.__subclasscheck__ #141171
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
_abc._abc_subclasscheckhas very poor performance and (I think) a memory leak #92810Alternative implementation of #131914 which doesn't modify class attributes on every
__subclasscheck__call. Instead it checks in__new__does current class havedef __subclasses__(cls)overriden, and if so, enables casefor scls in cls.__subclasses__(), which is disabled by default. To handle cases likeNumber -> Real -> Integral -> int; assert issubclass(int, Number) is True, method.register(subclass)is callingsuper().register(subclass)recursively.For benchmark from #131914:
isinstance(child, Parent)2MiB...15MiB
2MiB...15MiB
10MiB...23MiB
10MiB...23MiB
issubclass(Child, Parent)0MiB
0MiB...1MiB
6MiB...8MiB
5MiB...8MiB
isinstance(child, Grandparent)0MiB...2MiB
0MiB...1MiB
4MiB...6MiB
3MiB...6MiB
issubclass(Child, Grandparent)0MiB
0MiB
0MiB...2MiB
0MiB...1MiB
not isinstance(child, Sibling)2MiB...14MiB
1MiB...14MiB
14MiB...23MiB
13MiB...22MiB
not issubclass(Child, Sibling)0MiB
0MiB...1MiB
10MiB...12MiB
7MiB...10MiB
not isinstance(child, Cousin)1MiB...2MiB
1MiB...2MiB
8MiB...10MiB
6MiB...8MiB
not issubclass(Child, Cousin)0MiB
0MiB...1MiB
5MiB...6MiB
3MiB...4MiB
not isinstance(child, Uncle)5705MiB...6332MiB
0MiB...1MiB
3797MiB...4423MiB
5MiB
not issubclass(Child, Uncle)5704MiB
0MiB
3794MiB...3795MiB
3MiB
Memory increment is measured during
isinstance()/issubclass()calls, not during preparation, like class creation or registration.isinstance(child, Parent.register)0MiB
0MiB
0MiB
0MiB
issubclass(Child, Parent.register)0MiB
0MiB
0MiB
0MiB
isinstance(child, Grandparent.register)0MiB
0MiB
0MiB
0MiB
issubclass(Child, Grandparent.register)0MiB
0MiB
0MiB
0MiB
not isinstance(child, Sibling.register)0MiB
1MiB
0MiB
2MiB
not issubclass(Child, Sibling.register)0MiB
1MiB
0MiB
2MiB
not isinstance(child, Cousin.register)0MiB
2MiB
0MiB
3MiB
not issubclass(Child, Cousin.register)0MiB
2MiB
0MiB
3MiB
not isinstance(child, Uncle.register)0MiB
2MiB...3MiB
0MiB
4MiB
not issubclass(Child, Uncle.register)0MiB
2MiB
0MiB
4MiB
isinstance(child, Parent.__subclasses__)0MiB
0MiB
0MiB
0MiB
issubclass(Child, Parent.__subclasses__)0MiB
0MiB
0MiB
0MiB
isinstance(child, Grandparent.__subclasses__)0MiB
0MiB
0MiB
0MiB
issubclass(Child, Grandparent.__subclasses__)0MiB
0MiB
0MiB
0MiB
not isinstance(child, Sibling.__subclasses__)0MiB
0MiB
0MiB
1MiB
not issubclass(Child, Sibling.__subclasses__)0MiB
0MiB
0MiB
1MiB
not isinstance(child, Cousin.__subclasses__)0MiB
0MiB
0MiB
1MiB
not issubclass(Child, Cousin.__subclasses__)0MiB
0MiB
0MiB
1MiB
not isinstance(child, Uncle.__subclasses__)0MiB
0MiB
0MiB
2MiB
not issubclass(Child, Uncle.__subclasses__)0MiB
0MiB
0MiB
2MiB