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

Increase Enum performance #83283

Closed
MrMrRobat mannequin opened this issue Dec 20, 2019 · 4 comments
Closed

Increase Enum performance #83283

MrMrRobat mannequin opened this issue Dec 20, 2019 · 4 comments
Assignees
Labels
3.11 only security fixes pending The issue will be closed if no feedback is provided performance Performance or resource usage stdlib Python modules in the Lib dir

Comments

@MrMrRobat
Copy link
Mannequin

MrMrRobat mannequin commented Dec 20, 2019

BPO 39102
Nosy @warsaw, @methane, @ethanfurman, @ilevkivskyi, @jack1142, @MrMrRobat
PRs
  • bpo-39102: Increase Enum performance up to 10x times (3x average) #17669
  • Files
  • benchmark_result.txt: Benchmark-comparison
  • 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 = 'https://github.com/ethanfurman'
    closed_at = None
    created_at = <Date 2019-12-20.02:48:28.850>
    labels = ['library', '3.10', 'performance']
    title = 'Increase Enum performance'
    updated_at = <Date 2021-04-11.15:41:25.877>
    user = 'https://github.com/MrMrRobat'

    bugs.python.org fields:

    activity = <Date 2021-04-11.15:41:25.877>
    actor = 'ethan.furman'
    assignee = 'ethan.furman'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2019-12-20.02:48:28.850>
    creator = 'MrMrRobat'
    dependencies = []
    files = ['48793']
    hgrepos = []
    issue_num = 39102
    keywords = ['patch']
    message_count = 2.0
    messages = ['358694', '358742']
    nosy_count = 7.0
    nosy_names = ['barry', 'eli.bendersky', 'methane', 'ethan.furman', 'levkivskyi', 'jack1142', 'MrMrRobat']
    pr_nums = ['17669']
    priority = 'normal'
    resolution = None
    stage = 'needs patch'
    status = 'open'
    superseder = None
    type = 'performance'
    url = 'https://bugs.python.org/issue39102'
    versions = ['Python 3.10']

    @MrMrRobat
    Copy link
    Mannequin Author

    MrMrRobat mannequin commented Dec 20, 2019

    Now enum has very poor speed on trying values and attributes access (especially when it comes to accessing members name/value attrs)

    There are two major reasons why attrs access is slow:

    • All values/names access going through DynamicClassAttribute (x10 slower than accessing attr directly)
    • EnumMeta has __getattr__ which is slowing down even direct class attributes access (up to x6 slower)

    However, there are no need to use it, as we could just set value and name to newly created enum members without affecting its class.

    The main issue with values check is the slow _missing_ hook handling when it raising exception, which happens pretty much always, if value is not valid enum and we talking about vanilla Enum class.

    Also I found Flag performance issue being fixed already:
    https://bugs.python.org/issue38045
    It's also related, because new Flag creation involves many member.name lookups

    My proposal:

    • I think we should completely get rid of __getattr__ on Enum (~6x speed boost)
    • Rework DynamicClassAttribute so it could work without __getattr__ or perhaps completely get rid of it
    • Don't use DynamicClassAttribute for member.name and .value (~10x speed boost)
    • Think of faster handling of _missing_ hook (~2x speed boost)
    • Make other improvements to the code

    Proposed changes doesn't require changing public API or behaviour.

    By far I were able to implement almost things proposed here and will be happy to make a PR.

    @MrMrRobat MrMrRobat mannequin added 3.7 (EOL) end of life 3.8 (EOL) end of life 3.9 only security fixes stdlib Python modules in the Lib dir performance Performance or resource usage labels Dec 20, 2019
    @terryjreedy terryjreedy removed 3.7 (EOL) end of life 3.8 (EOL) end of life labels Dec 20, 2019
    @MrMrRobat
    Copy link
    Mannequin Author

    MrMrRobat mannequin commented Dec 20, 2019

    Also, do we need to leave compatibility with python <3.8?
    If not, we could use the fact that python 3.8 dicts and sets which are preserve order to speed things up even more. Also I'd replace % string formatting with f-strings, as they also faster.

    And another thing to think about: maybe we can calculate values returned by __str__, __repr__ and __invert__ once on member creation, since they not supposed to change during its life?

    For example __invert__ on Flag does a lot of work on every call:
    def __invert__(self):
    cls = self.__class__
    members, uncovered = _decompose(cls, self._value_)
    inverted = cls(0)
    for m in cls:
    if m not in members and not (m._value_ & self._value_):
    inverted = inverted | m
    return cls(inverted)

    @ethanfurman ethanfurman added 3.10 only security fixes and removed 3.9 only security fixes labels Apr 11, 2021
    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    @gpshead gpshead added 3.11 only security fixes and removed 3.10 only security fixes labels Apr 14, 2022
    @iritkatriel
    Copy link
    Member

    CC @mdboom .

    @ethanfurman
    Copy link
    Member

    I believe this issue is no longer relevant as the ideas have been incorporated in other PRs; I'll double check at some point.

    @iritkatriel iritkatriel added the pending The issue will be closed if no feedback is provided label Sep 9, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.11 only security fixes pending The issue will be closed if no feedback is provided performance Performance or resource usage stdlib Python modules in the Lib dir
    Projects
    None yet
    Development

    No branches or pull requests

    4 participants