Skip to content
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: Micro-optimise typing._get_protocol_attrs #103152

Merged
merged 2 commits into from
Mar 31, 2023

Conversation

AlexWaygood
Copy link
Member

@AlexWaygood AlexWaygood commented Mar 31, 2023

This PR is a bunch of micro-optimisations for typing._get_protocol_attrs that together have a significant impact on the speed of isinstance checks against runtime-checkable protocols.

Benchmark:
import time
from typing import Protocol, runtime_checkable

@runtime_checkable
class HasX(Protocol):
    x: int

class Foo:
    @property
    def x(self) -> int:
        return 42

class Bar:
    x = 42

class Baz:
    def __init__(self):
        self.x = 42

class Egg: ...

class Nominal(HasX):
    def __init__(self):
        self.x = 42

class Registered: ...

HasX.register(Registered)

num_instances = 500_000
foos = [Foo() for _ in range(num_instances)]
bars = [Bar() for _ in range(num_instances)]
bazzes = [Baz() for _ in range(num_instances)]
basket = [Egg() for _ in range(num_instances)]
nominals = [Nominal() for _ in range(num_instances)]
registereds = [Registered() for _ in range(num_instances)]


def bench(objs, title):
    start_time = time.perf_counter()
    for obj in objs:
        isinstance(obj, HasX)
    elapsed = time.perf_counter() - start_time
    print(f"{title}: {elapsed:.2f}")


bench(foos, "Time taken for objects with a property")
bench(bars, "Time taken for objects with a classvar")
bench(bazzes, "Time taken for objects with an instance var")
bench(basket, "Time taken for objects with no var")
bench(nominals, "Time taken for nominal subclass instances")
bench(registereds, "Time taken for registered subclass instances")

Results on dfc4c95:

Time taken for objects with a property: 2.17
Time taken for objects with a classvar: 2.10
Time taken for objects with an instance var: 2.13
Time taken for objects with no var: 2.47
Time taken for nominal subclass instances: 2.20
Time taken for registered subclass instances: 4.72

Results with this PR:

Time taken for objects with a property: 1.65
Time taken for objects with a classvar: 1.60
Time taken for objects with an instance var: 1.61
Time taken for objects with no var: 1.89
Time taken for nominal subclass instances: 1.61
Time taken for registered subclass instances: 3.79

I'm once again skipping NEWS, since #103034 will counteract the boost to performance here, so the overall impact on performance for runtime-checkable protocols in 3.12 is still TBD.

@AlexWaygood
Copy link
Member Author

May once again be of interest to @leycec and @posita :)

Copy link
Member

@carljm carljm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine to me.

Given @hauntsaninja 's feedback in #103141 , are you planning to land a single news entry for this and that one and #103034 that summarizes the changes and the overall perf impact?

Lib/typing.py Outdated Show resolved Hide resolved
Copy link
Contributor

@hauntsaninja hauntsaninja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hooray, thanks for working on this!

@AlexWaygood
Copy link
Member Author

Given @hauntsaninja 's feedback in #103141 , are you planning to land a single news entry for this and that one and #103034 that summarizes the changes and the overall perf impact?

Yeah, I'll try to rework the NEWS entry in #103034 to summarise the changes made in the performance-related PRs as well as that PR 👍

Copy link
Contributor

@hauntsaninja hauntsaninja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

@AlexWaygood AlexWaygood merged commit 361a3ea into python:main Mar 31, 2023
10 checks passed
@AlexWaygood AlexWaygood deleted the micro-optimise-protocols branch March 31, 2023 20:54
warsaw pushed a commit to warsaw/cpython that referenced this pull request Apr 11, 2023
…03152)

Improve performance of `isinstance()` checks against runtime-checkable protocols
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.12 bugs and security fixes performance Performance or resource usage skip news stdlib Python modules in the Lib dir topic-typing type-feature A feature request or enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants