This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: int.__repr__() is slower than repr()
Type: performance Stage: resolved
Components: Interpreter Core Versions: Python 3.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: methane, serhiy.storchaka, vstinner
Priority: normal Keywords: patch

Created on 2017-09-10 17:34 by serhiy.storchaka, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 3481 merged serhiy.storchaka, 2017-09-10 18:00
Messages (3)
msg301821 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-09-10 17:34
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
msg302685 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-09-21 11:25
New changeset 5e02c7826f9797fb3add79b608ef51f7a62b3e5a by Serhiy Storchaka in branch 'master':
bpo-31410: Optimized calling wrapper and classmethod descriptors. (#3481)
https://github.com/python/cpython/commit/5e02c7826f9797fb3add79b608ef51f7a62b3e5a
msg302688 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-09-21 12:14
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.
History
Date User Action Args
2022-04-11 14:58:52adminsetgithub: 75591
2017-09-21 12:14:11vstinnersetnosy: + methane
messages: + msg302688
2017-09-21 12:05:31serhiy.storchakasetstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2017-09-21 11:25:38serhiy.storchakasetmessages: + msg302685
2017-09-10 18:00:53serhiy.storchakasetkeywords: + patch
pull_requests: + pull_request3471
2017-09-10 17:34:20serhiy.storchakacreate