-
- Notifications
You must be signed in to change notification settings - Fork 1.3k
Speed-up build by avoiding a expensive operation #3395
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
Conversation
| code = f.read().replace(r'\r\n', r'\n') | ||
| | ||
| exec(compile(code, __file__, 'exec'), locals()) | ||
| exec(code, locals()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only difference I can think here is that if the setup script has a syntax error, compile would fail so no instruction gets executed. I don't know how exec works, but it could be the case it gets to execute something before failing... I don't think this is relevant though because the PEP 517 hooks are supposed to be executed in isolated subcommands, so it is fine for the hook to crash.
However, I might be failing to see other problems...
Alternatively we could also try.
try: exec(code, locals()) except Exception: exec(compile(code, __file__, 'exec'), locals())There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's my understanding that if there is a syntax error, nothing will ever be executed, so no concerns here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An important difference is that — when an exception gets raised — the stacktrace is now broken. The second argument to compile tells python where to find the source code when displaying the stacktrace. That's exactly what #3577 wanted to improve.
Calling compile before exec doesn't seem to have any noticeable effect on performance:
$ python -m timeit -s 'code = "1 + 1 #" + "A"*10000' 'exec(code)' 20000 loops, best of 5: 10.7 usec per loop $ python -m timeit -s 'code = "1 + 1 #" + "A"*10000' 'exec(compile(code, __file__, "exec"))' 20000 loops, best of 5: 10.7 usec per loop I don't see how that could make any difference. To execute code the code needs to get compiled before. It doesn't matter if compile gets executed explicitly.
| @jaraco, is there any chance you could have a look on this? |
The `exec` function in Python should be able to execute code directly. Using `compile` and then `exec` seem to cause an overhead.
1abc6db to 241b529 Compare | Part of the improvements in this PR was superseded by #3421 so I rebased the proposed changes. Now the PR only targets the |
build by avoiding 2 expensive operationsbuild by avoiding a expensive operation |
So the optim8zation flags should be the same. and |
I decided to investigate a little bit the execution time for
setuptools' PEP 517 backend after reading some criticism about its speed (it seems that it is much slower than the alternatives) on Python discourse and in other places. I don't really think the difference is relevant, but it would be nice if we can tackle the negative publicity.So I hacked together this dirty profiling script to see which parts of setuptools take longer to run.
The first time I run it, I saw the following:
(The bright pink shows the function I was selecting to see, in this case
exec).It seems that we are currently paying the price of running
execin most PEP 517 hooks twice... This was very curious, so I came back to a line in thebuild_metathat I never really understood:setuptools/setuptools/build_meta.py
Line 174 in 9a60016
In this line, we are compiling the setup script before executing it, which is curious. I don't know why this is necessary, since
execshould also be able to compile the code...The other part that catch my attention in the profile was
dist._install_dependencies(bright pink again):which seems to be one of the main contributors for the execution time.
When I was looking at the code for this function, I saw that we are trying to find out missing dependencies for entry-points that have
extras(which is not very common nowadays since pip simply ignore extras in entry-points).I decided to quickly hack these two code paths, and I was positively surprised by the huge impact:
(It is clear to me that the precise timings will vary from computer to computer and also depend on which programs the user has open in their machine... but this little experiment seems to be showing a general tendency).
This PR contains these 2 simple changes.
(I don't have a lot of experience with Python profiling, so I might also have implemented something wrong or underestimated something).
Summary of changes
build_meta: Avoid runningcompilebeforeexecdist: Avoid trying to install dependencies for entry-points that don't have extras.Closes
Pull Request Checklist
changelog.d/.(See documentation for details)