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: operator.py: move the Python implementation in the else block of try/except ImportError
Type: performance Stage:
Components: Versions: Python 3.4
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: Nosy List: Arfrever, christian.heimes, pitrou, skrah, vstinner, zach.ware
Priority: normal Keywords: patch

Created on 2013-10-11 23:49 by vstinner, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
operator.py vstinner, 2013-10-11 23:49
builtin_operator.patch vstinner, 2013-10-28 21:18 review
builtin_operator_diff.patch vstinner, 2013-10-28 21:18
Messages (10)
msg199525 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-10-11 23:49
To speedup Python startup, it may be interesting to not create useless many functions and classes in operator.py: "from _operator import *" will remove them a few line later.

What do you think of moving the Python implementation of the operator module inside in the else block of "try/except ImportError" section?

See attached operator.py for an example.

It adds an ugly level of indentation, but it's for performances!

Another option is to add a _pyoperator module.
msg199566 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2013-10-12 13:13
Using the microbenchmark I get (standard version):

./python -m timeit "import sys; modname='operator'" "__import__(modname); del sys.modules[modname]"
1000 loops, best of 3: 460 usec per loop


Victor's version:

./python -m timeit "import sys; modname='operator'" "__import__(modname); del sys.modules[modname]"
1000 loops, best of 3: 355 usec per loop


Importing _operator directly:

./python -m timeit "import sys; modname='_operator'" "__import__(modname); del sys.modules[modname]"
10000 loops, best of 3: 35.7 usec per loop


Extrapolating from what I did with decimal, I guess a _pyoperator
version could get down to something like 70 usec.
msg199701 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2013-10-13 12:18
To be fair, for the startup time I can't really detect any difference between importing _operator directly and the current setup.
msg201583 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-10-28 21:18
> Another option is to add a _pyoperator module.

Attached builtin_operator.patch patch implements this option: operator.c becomes the main operator module, _pyoperator is the pure Python implementation (don't use "from _operator import *" anymore).

With the patch:

$ ./python -m timeit "import sys; modname='_pyoperator'" "__import__(modname); del sys.modules[modname]" 
1000 loops, best of 3: 276 usec per loop

$ ./python -m timeit "import sys; modname='operator'" "__import__(modname); del sys.modules[modname]" 
10000 loops, best of 3: 22.7 usec per loop


The patch is huge because files are renamed: see builtin_operator_diff.patch for the diff.
msg201585 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-10-28 21:22
Without the patch:

$ ./python -m timeit "import sys; modname='operator'" "__import__(modname); del sys.modules[modname]" 
1000 loops, best of 3: 289 usec per loop

$ ./python -m timeit "import sys; modname='_operator'" "__import__(modname); del sys.modules[modname]" 
10000 loops, best of 3: 21.4 usec per loop


"import operator" is 12.7x faster (289 usec => 22.7 usec) with builtin_operator.patch.
msg201638 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2013-10-29 14:22
Why not:


try:
    from _operator import *
except ImportError:
    from _pyoperator import *
msg201639 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-10-29 14:31
> Why not:
> 
> try:
>     from _operator import *
> except ImportError:
>     from _pyoperator import *

Let's try (I replaced operator.py with these 4 lines).

$ ./python -m timeit "import sys; modname='operator'" "__import__(modname); del sys.modules[modname]; del sys.modules['_operator']" 
10000 loops, best of 3: 165 usec per loop

$ ./python -m timeit "import sys; modname='operator'" "__import__(modname); del sys.modules[modname]" 
10000 loops, best of 3: 136 usec per loop

"import operator" is only 2x faster (289 usec => 136 usec). It's less interesting. And what would be the purpose of a file of 4 line which containing "import *"? Do you think that PyPy, IronPython and Jython will reuse such trampoline/wrapper?
msg201640 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2013-10-29 14:37
> "import operator" is only 2x faster (289 usec => 136 usec). It's less
> interesting.

The real question for me is: why are you interested in speeding up the
import of the operator module? 200 µs won't make a visible difference.

> And what would be the purpose of a file of 4 line which
> containing "import *"?

To make it obvious that there are two implementations, one of which is
a fallback.
msg201641 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2013-10-29 14:45
I understand the desire to speed things up, but either the four-line facade
module or else a single module is probably required by PEP-399.
msg206022 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-12-13 02:57
"The real question for me is: why are you interested in speeding up the
import of the operator module? 200 µs won't make a visible difference."

Alone, the gain is useless, but it's like the work done in Python 3.4 to avoid loading some modules at startup. The overall idea is to have a fast startup time.

I heard that Python 3 startup time is a major blocker point for Mercurial for example.

But maybe this specific issue is not worth the trouble. (Other parts should be optimized.)
History
Date User Action Args
2022-04-11 14:57:51adminsetgithub: 63428
2013-12-16 23:19:57vstinnersetstatus: open -> closed
resolution: wont fix
2013-12-13 02:57:07vstinnersetmessages: + msg206022
2013-10-29 14:45:50skrahsetmessages: + msg201641
title: operator.py: move the Python implementation in the else block of try/except ImportError -> operator.py: move the Python implementation in the else block of try/except ImportError
2013-10-29 14:37:21pitrousetmessages: + msg201640
title: operator.py: move the Python implementation in the else block of try/except ImportError -> operator.py: move the Python implementation in the else block of try/except ImportError
2013-10-29 14:31:52vstinnersettype: performance
messages: + msg201639
2013-10-29 14:22:23pitrousetnosy: + pitrou
messages: + msg201638
2013-10-28 21:22:18vstinnersetmessages: + msg201585
2013-10-28 21:18:39vstinnersetfiles: + builtin_operator_diff.patch
2013-10-28 21:18:28vstinnersetfiles: + builtin_operator.patch
keywords: + patch
messages: + msg201583
2013-10-18 16:54:01zach.waresetnosy: + zach.ware
2013-10-13 16:31:32Arfreversetnosy: + Arfrever
2013-10-13 12:18:28skrahsetmessages: + msg199701
2013-10-12 13:13:46skrahsetnosy: + skrah
messages: + msg199566
2013-10-11 23:49:11vstinnercreate