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

int.__repr__() is slower than repr() #75591

Closed
serhiy-storchaka opened this issue Sep 10, 2017 · 3 comments
Closed

int.__repr__() is slower than repr() #75591

serhiy-storchaka opened this issue Sep 10, 2017 · 3 comments
Labels
3.7 (EOL) end of life interpreter-core (Objects, Python, Grammar, and Parser dirs) performance Performance or resource usage

Comments

@serhiy-storchaka
Copy link
Member

BPO 31410
Nosy @vstinner, @methane, @serhiy-storchaka
PRs
  • bpo-31410: Optimized calling wrapper and classmethod descriptors. #3481
  • 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 = <Date 2017-09-21.12:05:31.980>
    created_at = <Date 2017-09-10.17:34:20.389>
    labels = ['interpreter-core', '3.7', 'performance']
    title = 'int.__repr__() is slower than repr()'
    updated_at = <Date 2017-09-21.12:14:11.657>
    user = 'https://github.com/serhiy-storchaka'

    bugs.python.org fields:

    activity = <Date 2017-09-21.12:14:11.657>
    actor = 'vstinner'
    assignee = 'none'
    closed = True
    closed_date = <Date 2017-09-21.12:05:31.980>
    closer = 'serhiy.storchaka'
    components = ['Interpreter Core']
    creation = <Date 2017-09-10.17:34:20.389>
    creator = 'serhiy.storchaka'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 31410
    keywords = ['patch']
    message_count = 3.0
    messages = ['301821', '302685', '302688']
    nosy_count = 3.0
    nosy_names = ['vstinner', 'methane', 'serhiy.storchaka']
    pr_nums = ['3481']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'performance'
    url = 'https://bugs.python.org/issue31410'
    versions = ['Python 3.7']

    @serhiy-storchaka
    Copy link
    Member Author

    Following the StackOverflow question [1].

    Calling repr() is faster than calling unbound method __repr__(). This looks strange at first glance because it is *obvious* that repr() is implemented via calling __repr__().

    $ ./python -m timeit "''.join(map(repr, range(10000)))"
    500 loops, best of 5: 809 usec per loop
    
    $ ./python -m timeit "''.join(map(int.__repr__, range(10000)))"
    200 loops, best of 5: 1.27 msec per loop

    Actually repr() just called the tp_repr slot, while calling int.__repr__ passes through many intermediate layers.

    Proposed PR gets rid of a half of the overhead. It avoids creating and calling an itermediate function object. The result still is slower then calling repr().

    $ ./python -m timeit "''.join(map(int.__repr__, range(10000)))"
    200 loops, best of 5: 1.01 msec per loop

    The PR also speeds up calling classmethod descriptors.

    $ ./python -m timeit -s "cm = bytes.fromhex; args = [('',)]*10000; from itertools import starmap" -- "b''.join(starmap(cm, args))"
    500 loops, best of 5: 515 usec per loop
    
    
    $ ./python -m timeit -s "cm = bytes.__dict__['fromhex']; args = [(bytes, '')]*10000; from itertools import starmap" -- "b''.join(starmap(cm, args))"
    500 loops, best of 5: 704 usec per loop

    Patched:

    $ ./python -m timeit -s "cm = bytes.__dict__['fromhex']; args = [(bytes, '')]*10000; from itertools import starmap" -- "b''.join(starmap(cm, args))"
    500 loops, best of 5: 598 usec per loop

    [1] https://stackoverflow.com/questions/45376719/why-is-reprint-faster-than-strint

    @serhiy-storchaka serhiy-storchaka added 3.7 (EOL) end of life interpreter-core (Objects, Python, Grammar, and Parser dirs) performance Performance or resource usage labels Sep 10, 2017
    @serhiy-storchaka
    Copy link
    Member Author

    New changeset 5e02c78 by Serhiy Storchaka in branch 'master':
    bpo-31410: Optimized calling wrapper and classmethod descriptors. (bpo-3481)
    5e02c78

    @vstinner
    Copy link
    Member

    Oh, nice optimization!

    I see that you reused the _PyMethodDef_RawFastCallDict() function that I added exactly for the same reason: prevent the creation of a temporary C function only created for a single call and then destroyed.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.7 (EOL) end of life interpreter-core (Objects, Python, Grammar, and Parser dirs) performance Performance or resource usage
    Projects
    None yet
    Development

    No branches or pull requests

    2 participants