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: Further optimise typing._ProtocolMeta.__instancecheck__ #103280

Merged
merged 1 commit into from
Apr 5, 2023

Conversation

AlexWaygood
Copy link
Member

@AlexWaygood AlexWaygood commented Apr 5, 2023

I probably should have spotted this when doing #103034! This provides some more speedup for isinstance() checks against runtime-checkable protocols. The improvement is small, but noticeable.

Benchmarks on main:

Time taken for objects with a property: 2.53
Time taken for objects with a classvar: 2.59
Time taken for objects with an instance var: 3.18
Time taken for objects with no var: 2.38
Time taken for nominal subclass instances: 0.19
Time taken for registered subclass instances: 0.46

Benchmarks with this PR:

Time taken for objects with a property: 2.49
Time taken for objects with a classvar: 2.49
Time taken for objects with an instance var: 3.06
Time taken for objects with no var: 2.36
Time taken for nominal subclass instances: 0.18
Time taken for registered subclass instances: 0.47
Benchmark script
import time
from typing import Protocol, runtime_checkable

@runtime_checkable
class HasX(Protocol):
    x: int
    def foo(self): ...

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

class Bar:
    x = 42
    def foo(self): ...

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

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")

@AlexWaygood AlexWaygood added type-feature A feature request or enhancement performance Performance or resource usage stdlib Python modules in the Lib dir topic-typing 3.12 bugs and security fixes labels Apr 5, 2023
@AlexWaygood AlexWaygood requested a review from carljm April 5, 2023 15:00
@AlexWaygood
Copy link
Member Author

AlexWaygood commented Apr 5, 2023

Continuing to skip news for now until we've reached a decision on whether we want to consider more radical optimisations that would change behaviour (#74690 (comment)). Once I'm confident we've finished optimising this code, I'll add Whatsnew/Docs/NEWS entries for all of the optimisation PRs together :)

@AlexWaygood AlexWaygood merged commit de18267 into python:main Apr 5, 2023
10 checks passed
@AlexWaygood AlexWaygood deleted the more-optimisation branch April 5, 2023 16:37
@bedevere-bot

This comment was marked as off-topic.

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

3 participants