Skip to content

Conversation

@dolfinus
Copy link

@dolfinus dolfinus commented Nov 6, 2025

Alternative implementation of #131914 which doesn't modify class attributes on every __subclasscheck__ call. Instead it checks in __new__ does current class have def __subclasses__(cls) overriden, and if so, enables case for scls in cls.__subclasses__(), which is disabled by default. To handle cases like Number -> Real -> Integral -> int; assert issubclass(int, Number) is True, method .register(subclass) is calling super().register(subclass) recursively.

For benchmark from #131914:

sudo ./python -m pyperf system tune
taskset -c 0 ./python benchmark.py --metaclass abc.ABCMeta --rounds 3 --classes 5000
taskset -c 0 ./python benchmark.py --metaclass _py_abc.ABCMeta --rounds 3 --classes 5000
Impl Max memory before, MB Max memory after, MB
_abc 6332 47
_py_abc 4423 58
Impl Total time before Total time after
_abc 19m 57s 2m 6s
_py_abc 17m 14s 6m 14s
Check Impl before after Impl before after
isinstance(child, Parent) _abc 0.191us...0.213us
2MiB...15MiB
0.191us...0.193us
2MiB...15MiB
_py_abc 0.334us...0.348us
10MiB...23MiB
0.354us...0.362us
10MiB...23MiB
issubclass(Child, Parent) _abc 0.175us...0.182us
0MiB
0.173us...0.174us
0MiB...1MiB
_py_abc 0.328us...0.329us
6MiB...8MiB
0.346us...0.363us
5MiB...8MiB
isinstance(child, Grandparent) _abc 0.184us...0.188us
0MiB...2MiB
0.189us...0.192us
0MiB...1MiB
_py_abc 0.330us...0.333us
4MiB...6MiB
0.350us...0.356us
3MiB...6MiB
issubclass(Child, Grandparent) _abc 0.166us...0.169us
0MiB
0.168us...0.172us
0MiB
_py_abc 0.326us...0.329us
0MiB...2MiB
0.324us...0.356us
0MiB...1MiB
not isinstance(child, Sibling) _abc 0.184us...0.187us
2MiB...14MiB
0.188us...0.206us
1MiB...14MiB
_py_abc 0.564us...0.576us
14MiB...23MiB
0.628us...0.788us
13MiB...22MiB
not issubclass(Child, Sibling) _abc 0.169us...0.169us
0MiB
0.171us...0.172us
0MiB...1MiB
_py_abc 0.531us...0.540us
10MiB...12MiB
0.555us...0.570us
7MiB...10MiB
not isinstance(child, Cousin) _abc 0.186us...0.186us
1MiB...2MiB
0.185us...0.188us
1MiB...2MiB
_py_abc 0.572us...0.589us
8MiB...10MiB
0.607us...0.627us
6MiB...8MiB
not issubclass(Child, Cousin) _abc 0.170us...0.172us
0MiB
0.169us...0.170us
0MiB...1MiB
_py_abc 0.534us...0.541us
5MiB...6MiB
0.558us...0.569us
3MiB...4MiB
not isinstance(child, Uncle) _abc 8.188us...8.728us
5705MiB...6332MiB
0.193us...0.207us
0MiB...1MiB
_py_abc 13.098us...13.243us
3797MiB...4423MiB
0.607us...0.656us
5MiB
not issubclass(Child, Uncle) _abc 8.071us...8.358us
5704MiB
0.176us...0.179us
0MiB
_py_abc 13.292us...13.371us
3794MiB...3795MiB
0.556us...0.582us
3MiB

Memory increment is measured during isinstance() / issubclass() calls, not during preparation, like class creation or registration.

Check Impl before after Impl before after
isinstance(child, Parent.register) _abc 0.388us...0.390us
0MiB
0.402us...0.406us
0MiB
_py_abc 0.688us...0.693us
0MiB
0.795us...0.854us
0MiB
issubclass(Child, Parent.register) _abc 0.240us...0.242us
0MiB
0.252us...0.256us
0MiB
_py_abc 0.681us...0.689us
0MiB
0.729us...0.763us
0MiB
isinstance(child, Grandparent.register) _abc 0.184us
0MiB
0.190us...0.192us
0MiB
_py_abc 0.393us...0.397us
0MiB
0.426us...0.461us
0MiB
issubclass(Child, Grandparent.register) _abc 0.170us...0.172us
0MiB
0.168us...0.171us
0MiB
_py_abc 0.384us...0.399us
0MiB
0.392us...0.513us
0MiB
not isinstance(child, Sibling.register) _abc 0.044us
0MiB
0.041us...0.052us
1MiB
_py_abc 0.042us...0.043us
0MiB
0.043us...0.048us
2MiB
not issubclass(Child, Sibling.register) _abc 0.028us
0MiB
0.028us
1MiB
_py_abc 0.028us
0MiB
0.029us...0.029us
2MiB
not isinstance(child, Cousin.register) _abc 0.044us
0MiB
0.041us...0.042us
2MiB
_py_abc 0.044us
0MiB
0.044us...0.045us
3MiB
not issubclass(Child, Cousin.register) _abc 0.028us
0MiB
0.028us
2MiB
_py_abc 0.028us
0MiB
0.027us...0.029us
3MiB
not isinstance(child, Uncle.register) _abc 0.372us...0.382us
0MiB
0.367us...0.375us
2MiB...3MiB
_py_abc 1.352us...1.434us
0MiB
1.588us...1.629us
4MiB
not issubclass(Child, Uncle.register) _abc 0.356us...0.365us
0MiB
0.358us...0.365us
2MiB
_py_abc 1.306us...1.316us
0MiB
1.483us...1.544us
4MiB
Check Impl before after Impl before after
isinstance(child, Parent.__subclasses__) _abc 0.202us...0.209us
0MiB
0.210us...0.212us
0MiB
_py_abc 0.463us...0.468us
0MiB
0.483us...0.505us
0MiB
issubclass(Child, Parent.__subclasses__) _abc 0.189us...0.194us
0MiB
0.194us...0.196us
0MiB
_py_abc 0.457us...0.460us
0MiB
0.476us...0.497us
0MiB
isinstance(child, Grandparent.__subclasses__) _abc 0.199us...0.200us
0MiB
0.203us...0.212us
0MiB
_py_abc 0.463us...0.468us
0MiB
0.490us...0.531us
0MiB
issubclass(Child, Grandparent.__subclasses__) _abc 0.184us...0.188us
0MiB
0.185us...0.189us
0MiB
_py_abc 0.451us...0.455us
0MiB
0.472us...0.499us
0MiB
not isinstance(child, Sibling.__subclasses__) _abc 0.044us
0MiB
0.042us
0MiB
_py_abc 0.043us
0MiB
0.045us...0.045us
1MiB
not issubclass(Child, Sibling.__subclasses__) _abc 0.028us
0MiB
0.028us...0.029us
0MiB
_py_abc 0.027us...0.028us
0MiB
0.029us
1MiB
not isinstance(child, Cousin.__subclasses__) _abc 0.044us...0.046us
0MiB
0.042us
0MiB
_py_abc 0.044us
0MiB
0.043us...0.045us
1MiB
not issubclass(Child, Cousin.__subclasses__) _abc 0.028us
0MiB
0.028us
0MiB
_py_abc 0.028us
0MiB
0.029us
1MiB
not isinstance(child, Uncle.__subclasses__) _abc 0.233us...0.238us
0MiB
0.238us...0.243us
0MiB
_py_abc 0.878us...0.884us
0MiB
0.935us...1.009us
2MiB
not issubclass(Child, Uncle.__subclasses__) _abc 0.216us...0.223us
0MiB
0.218us...0.219us
0MiB
_py_abc 0.835us...0.845us
0MiB
0.857us...0.915us
2MiB

@bedevere-app
Copy link

bedevere-app bot commented Nov 6, 2025

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

@dolfinus dolfinus changed the title Improvement/abc meta subclasscheck v2 gh-92810: Reduce memory usage by ABCMeta.__subclasscheck__ (v2) Nov 6, 2025
@bedevere-app
Copy link

bedevere-app bot commented Nov 6, 2025

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

1 similar comment
@bedevere-app
Copy link

bedevere-app bot commented Nov 6, 2025

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

@dolfinus dolfinus changed the title gh-92810: Reduce memory usage by ABCMeta.__subclasscheck__ (v2) gh-92810: Reduce memory usage by ABCMeta.__subclasscheck__ Nov 6, 2025
@bedevere-app
Copy link

bedevere-app bot commented Nov 6, 2025

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

@dolfinus dolfinus force-pushed the improvement/ABCMeta_subclasscheck_v2 branch 9 times, most recently from 3e124f2 to b3bbd15 Compare December 1, 2025 00:09
@dolfinus dolfinus force-pushed the improvement/ABCMeta_subclasscheck_v2 branch from b3bbd15 to a7ef5b0 Compare December 1, 2025 06:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant