Repository navigation
Performance of typing._ProtocolMeta._get_protocol_attrs and isinstance #74690
Copy link
Copy link
Closed
Labels
3.12only security fixesonly security fixesperformancePerformance or resource usagePerformance or resource usagestdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-typing
Description
| BPO | 30505 |
|---|---|
| Nosy | @ilevkivskyi, @orenbenkiki |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee = None
closed_at = None
created_at = <Date 2017-05-29.09:16:49.884>
labels = ['library', 'performance']
title = 'Performance of typing._ProtocolMeta._get_protocol_attrs and isinstance'
updated_at = <Date 2017-06-02.17:44:59.311>
user = 'https://github.com/orenbenkiki'bugs.python.org fields:
activity = <Date 2017-06-02.17:44:59.311>
actor = 'levkivskyi'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Library (Lib)']
creation = <Date 2017-05-29.09:16:49.884>
creator = 'orenbenkiki'
dependencies = []
files = []
hgrepos = []
issue_num = 30505
keywords = []
message_count = 2.0
messages = ['294686', '295044']
nosy_count = 2.0
nosy_names = ['levkivskyi', 'orenbenkiki']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = 'performance'
url = 'https://bugs.python.org/issue30505'
versions = ['Python 3.6']Linked PRs
- gh-74690: typing: Don't unnecessarily call
_get_protocol_attrstwice in_ProtocolMeta.__instancecheck__#103141 - gh-74690: Micro-optimise
typing._get_protocol_attrs#103152 - gh-74690: typing: Simplify and optimise
_ProtocolMeta.__instancecheck__#103159 - gh-74690: typing: Call
_get_protocol_attrsand_callable_members_onlyat protocol class creation time, not duringisinstance()checks #103160 - gh-74690: Further optimise
typing._ProtocolMeta.__instancecheck__#103280 - gh-74690:
typing._ProtocolMeta.__instancecheck__: Exit early for protocols that only have callable members #103310 - gh-74690: Add more tests for runtime-checkable protocols #103347
- gh-74690: Document changes made to runtime-checkable protocols in 3.12 #103348
- [3.11] gh-74690: Add more tests for runtime-checkable protocols (GH-103347) #103350
- gh-74690: Don't set special protocol attributes on non-protocol subclasses of protocols #104622
- gh-74690: Make a typing test more resilient #104691
- gh-74690: Improve performance when testing against protocols with many attributes. #112157
- gh-74690: Optimise
isinstance()andissubclass()calls against runtime-checkable protocols by avoiding costlysuper()calls #112708 - gh-74690: Avoid a costly type check where possible in
_ProtocolMeta.__subclasscheck__#112717
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
3.12only security fixesonly security fixesperformancePerformance or resource usagePerformance or resource usagestdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-typing
Activity
orenbenkiki commentedon May 29, 2017
In 3.6.0, invocation of isinstance calls typing._ProtocolMeta._get_protocol_attrs.
This creates a set of all attributes in all base classes, loops on these attributes to check they exist, and discards the set. It is very slow.
My program uses isinstance to allow for flexibility in parameter types in certain key functions. I realize that using isinstance is frowned upon, but it seems to make sense in my case.
As a result, >95% of its run-time is inside typing._ProtocolMeta._get_protocol_attrs (!).
I have created a simple wrapper around isinstance which caches its result with a Dict[Tuple[type, type], bool]. This solved the performance problem, but introduced a different problem - type checking.
I use mypy and type annotations, and my code cleanly type-checks (with the occasional # type: ignore). If I switch to using my own isinstance function, then mypy's type inference no longer treats it as special, so it starts complaining about all uses of values protected by if isinstance(value, SomeType): ...
I propose that either the private typing._ProtocolMeta.__subclasscheck__ (which invokes _get_protocol_attrs), or the public isinstance, would be modified to cache their results.
I can create a PR for either approach, if this is acceptable.
ilevkivskyi commentedon Jun 2, 2017
Thanks for reporting!
The runtime implementation of protocol classes will be thoroughly reworked as a part of PEP-544, see also python/typing#417 for a proof of concept runtime implementation.
Also, there is another ongoing discussion python/typing#432 about a global refactoring of typing module that will significantly improve performance.
Therefore, I would wait with any large PRs until these two stories are settled. If you still want to propose a small PR, you can do this at the upstream typing repo https://github.com/python/typing
iritkatriel commentedon Sep 7, 2022
@orenbenkiki @ilevkivskyi What is the status of this issue, five years on?
ilevkivskyi commentedon Sep 7, 2022
TBH I am not sure this issue is still relevant (at least I didn't hear any recent complaints about
Protocolperformance). @orenbenkiki is performance still bad for your application?AlexWaygood commentedon Sep 7, 2022
I've seen recent complaints about the performance of
Protocolfrom users of thebeartypethird-party library, and by @posita here. (I'm not sure I've ever seen a proper write-up of the performance problems in a CPython issue, however.)ilevkivskyi commentedon Sep 7, 2022
Hm, actually the short-circuiting proposed in that comment may be a good idea (unless I forgot something) for a simple perf optimization (btw FWIW this is what mypy does internally, it always checks nominal subtypig first).
AlexWaygood commentedon Sep 7, 2022
@posita appears to have implemented optimised versions of
ProtocolMetahere and here.(I haven't studied the code for either link, but may be worth looking at!)
posita commentedon Sep 7, 2022
For color, the current implementation lives in Beartype and is divided among two classes:
beartype.typing._typingpep544._CachingProtocolMeta; andbeartype.typing._typingpep544.ProtocolThe meat is in
_CachingProtocolMetawhereas theProtocolis basically a hack to work around some standard library implementation details and could likely be eliminated. At a high level,_CachingProtocolMetaoverrides__instancecheck__to perform an examination of whether an object "satisfies" a Protocol definition similar totyping._ProtocolMeta.__instancecheck__and then caches the result by that object's type. Note the asymmetry: the examination is performed on the object, but it is assumed that all objects of the first-examined object's type are consistent. This makes the implementation ill-suited for runtime-composed "types" or monkey-patched objects.Happy to discuss further, if desired.
ilevkivskyi commentedon Sep 7, 2022
Caching may need a more careful consideration (especially w.r.t. monkey-patching classes). I was specifically referring to this idea
@posita Do you have any good code base where you can benchmark this w.r.t. current implementation?
posita commentedon Oct 9, 2022
I can probably do something crude with
numerary's anddyce's respective batteries of unit tests. Those are what motivatedbeartype.typing._typingpep544._CachingProtocolMetain the first place. Give me a few days and I can probably throw something together.43 remaining items
Merge branch 'main' into nestedcomps
gh-74690: Make a typing test more resilient (#104691)
python3-typing-extensions: upgrade 4.5.0 -> 4.6.2
python3-typing-extensions: upgrade 4.5.0 -> 4.6.2
python3-typing-extensions: upgrade 4.5.0 -> 4.6.2
Update dependency typing-extensions to v4.6.3 (#224)
pythongh-74690: Optimise `isinstance()` and `issubclass()` calls agai…
gh-74690: Optimise `isinstance()` and `issubclass()` calls against ru…
pythongh-74690: Avoid a costly type check where possible in `_Protoco…
gh-74690: Avoid a costly type check where possible in `_ProtocolMeta.…
pythongh-74690: Optimise `isinstance()` and `issubclass()` calls agai…
pythongh-74690: Avoid a costly type check where possible in `_Protoco…
namka279 commentedon Jul 21, 2024
pythongh-74690: Optimise `isinstance()` and `issubclass()` calls agai…
pythongh-74690: Avoid a costly type check where possible in `_Protoco…