GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Optimise `inspect.getattr_static` · Issue #103193 · python/cpython

Optimise inspect.getattr_static #103193

New issue
Copy link
New issue
Copy link
Closed
Closed
Optimise inspect.getattr_static#103193
Copy link
Assignees
AlexWaygood
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/ directorytype-featureA feature request or enhancementA feature request or enhancement

Description

@AlexWaygood
AlexWaygood
opened on Apr 2, 2023
Issue body actions

Feature or enhancement

Following 6d59c9e, typing._ProtocolMeta.__instancecheck__ uses inspect.getattr_static in a tight loop, where it had previously used hasattr. This improves semantics in several highly desirable ways, but causes a considerable slowdown for _ProtocolMeta.__instancecheck__, as inspect.getattr_static is much slower than hasattr.

The performance hit to _ProtocolMeta.__instancecheck__ has already been mostly mitigated through several typing-specific optimisations that are tracked in this issue:

  • Performance of typing._ProtocolMeta._get_protocol_attrs and isinstance #74690

However, it would be good to also see if we can improve the performance of inspect.getattr_static. This will not only improve the performance of isinstance() checks against classes subclassing typing.Protocol. It will also improve the performance of all other tools that use inspect.getattr_static for introspection without side effects.

Linked PRs

  • gh-103193: Micro-optimise helper functions for inspect.getattr_static #103195
  • gh-103193: Use LBYL idioms rather than EAFP in inspect.getattr_static #103318
  • gh-103193: Speedup and inline inspect._is_type #103321
  • gh-103193: Celebrate performance improvements to inspect.getattr_static in 'What's New in Python 3.12' #103349
  • gh-103193: cache calls to inspect._shadowed_dict in inspect.getattr_static #104267
  • gh-103193: Improve getattr_static test coverage #104286
  • [3.11] gh-103193: Improve getattr_static test coverage (GH-104286) #104290
  • gh-103193: Fix refleaks in test_inspect and test_typing #104320

Activity

Next
  1. AlexWaygood
    added
    type-featureA feature request or enhancementA feature request or enhancement
    performancePerformance or resource usagePerformance or resource usage
    stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
    3.12only security fixesonly security fixes
    on Apr 2, 2023
  2. AlexWaygood
    added 2 commits that reference this issue on Apr 2, 2023

    pythongh-103193: Micro-optimise helper functions for `inspect.getattr…

    469e165

    gh-103193: Micro-optimise helper functions for `inspect.getattr_stati…

    264c00a
  3. AlexWaygood commented on Apr 5, 2023

    @AlexWaygood
    AlexWaygood
    on Apr 5, 2023
    MemberAuthor
    More actions

    I can't see anything further that can be easily optimised here, but happy to reopen if somebody else can!

  4. AlexWaygood
    closed this as completedon Apr 5, 2023
  5. sobolevn commented on Apr 5, 2023

    @sobolevn
    sobolevn
    on Apr 5, 2023
    Member
    More actions

    I've measured my ideas, but the results are close to noise.

    Initial numbers:

    type[Foo]                :  793 ±  1 ns 
    Foo                      : 1852 ±  3 ns 
    type[Bar]                :  791 ±  1 ns 
    Bar                      : 1843 ±  2 ns 
    WithParentClassX         : 2768 ±  2 ns 
    Baz                      : 2712 ±  8 ns 
    WithParentX              : 3611 ±  9 ns 
    type[Missing]            : 3364 ± 13 ns 
    Missing                  : 2749 ± 22 ns 
    Slotted                  : 2363 ±  7 ns 
    Method                   : 1863 ± 11 ns 
    StMethod                 : 1863 ±  8 ns 
    ClsMethod                : 1847 ±  4 ns 
    

    After all changes:

    type[Foo]                :  793 ±  2 ns 
    Foo                      : 1833 ±  2 ns 
    type[Bar]                :  793 ±  3 ns 
    Bar                      : 1833 ±  5 ns 
    WithParentClassX         : 2756 ±  4 ns 
    Baz                      : 2701 ±  8 ns 
    WithParentX              : 3696 ± 28 ns 
    type[Missing]            : 3390 ± 41 ns 
    Missing                  : 2703 ±  9 ns 
    Slotted                  : 2312 ± 11 ns 
    Method                   : 1860 ± 19 ns 
    StMethod                 : 1828 ±  5 ns 
    ClsMethod                : 1859 ± 23 ns
    

    Code:

    from timeit import timeit
    from statistics import mean, stdev
    from inspect import getattr_static
    
    class Foo:
        @property
        def x(self) -> int:
            return 42
    
    class Bar:
        x = 42
    
    class WithParentClassX(Bar): ...
    
    class Baz:
        def __init__(self):
            self.x = 42
    
    class WithParentX(Baz): ...
    
    class Missing: ...
    
    class Slotted:
        __slots__ = ('x',)
        def __init__(self):
            self.x = 42
    
    class Method:
        def x(self): ...
    
    class ClsMethod:
        @classmethod
        def x(cls): ...
    
    class StMethod:
        @staticmethod
        def x(): ...
    
    import gc
    gc.disable()
    
    times = []
    def stats():
        ts = [t * 1e8 for t in sorted(times)[:5]]
        return f'{round(mean(ts)):4} ± {round(stdev(ts)):2} ns '
    
    def bench(obj):
        # Warmup:
        for _ in range(5):
            number = 100
            timeit(lambda: getattr_static(obj, 'x', None), number=number)
    
        # Actual bench:
        for _ in range(50):
            number = 1000
            t = timeit(lambda: getattr_static(obj, 'x', None), number=number) / number
            times.append(t)
    
        bench_name = (
            f'type[{obj.__name__}]'
            if isinstance(obj, type)
            else obj.__class__.__name__
        )
        print(f"{bench_name: <25}: {stats()}")
        times.clear()
    
    
    bench(Foo)
    bench(Foo())
    bench(Bar)
    bench(Bar())
    bench(WithParentClassX())
    bench(Baz())
    bench(WithParentX())
    bench(Missing)
    bench(Missing())
    bench(Slotted())
    bench(Method())
    bench(StMethod())
    bench(ClsMethod())
    Reacted by Alex Waygood
  6. AlexWaygood
    reopened this on Apr 6, 2023
  7. AlexWaygood
    added 4 commits that reference this issue on Apr 6, 2023

    pythongh-103193: Use LBYL idioms rather than EAFP in `inspect.getattr…

    972db3a

    gh-103193: Use LBYL idioms rather than EAFP in `inspect.getattr_stati…

    affedee

    pythongh-103193: Speedup and inline `inspect._is_type`

    1bacc91

    gh-103193: Speedup and inline `inspect._is_type` (#103321)

    dca7d17
  8. AlexWaygood commented on Apr 6, 2023

    @AlexWaygood
    AlexWaygood
    on Apr 6, 2023
    MemberAuthor
    More actions

    I think I'm once again out of ideas, at least for now.

  9. 10 remaining items

  10. AlexWaygood
    added 2 commits that reference this issue on May 8, 2023

    pythongh-103193: Improve `getattr_static` test coverage

    080871a

    gh-103193: Improve `getattr_static` test coverage (#104286)

    921185e
  11. miss-islington
    added a commit that references this issue on May 8, 2023

    pythongh-103193: Improve `getattr_static` test coverage (pythonGH-104286

    06532ae
  12. AlexWaygood
    added a commit that references this issue on May 8, 2023

    [3.11] gh-103193: Improve `getattr_static` test coverage (GH-104286) (#…

    d54f644
  13. jbower-fb
    added 2 commits that reference this issue on May 8, 2023

    pythongh-103193: cache calls to `inspect._shadowed_dict` in `inspect.…

    86ca8d2

    pythongh-103193: Improve `getattr_static` test coverage (python#104286)

    7714382
  14. carljm
    added a commit that references this issue on May 9, 2023

    Merge branch 'main' into inlinecomp2

    76077cd
  15. AlexWaygood
    added a commit that references this issue on May 9, 2023

    pythongh-103193: Fix refleaks in `test_inspect` and `test_typing`

    e52cce6
  16. kumaraditya303
    added a commit that references this issue on May 9, 2023

    gh-103193: Fix refleaks in `test_inspect` and `test_typing` (#104320)

    9196da4
  17. carljm
    added 4 commits that reference this issue on May 9, 2023

    Merge branch 'main' into inlinecomp2

    95401fe

    Merge branch 'main' into fixsendfamily

    a614216

    Merge branch 'main' into supermon

    c48eee3

    Merge branch 'main' into fixloadsuperspec

    cf11908
  18. AlexWaygood
    self-assigned this
    on May 31, 2023
  19. namka279 commented on Jul 21, 2024

    @namka279
    namka279
    on Jul 21, 2024 · Hidden as spam
    show commentMore actions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

  • @AlexWaygood
    AlexWaygood

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/ directorytype-featureA feature request or enhancementA feature request or enhancement

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.