pyenv-virtualenv: Slow shell performance after running pyenv virtualenv-init
It looks like there is some issue on my system that is causing very poor runtime performance after running pyenv virtualenv-init
. Related to #132
I think the issue is with pyenv verision-name
. What steps can I take to debug this further?
About this issue
- Original URL
- State: open
- Created 6 years ago
- Reactions: 35
- Comments: 54 (5 by maintainers)
Commits related to this issue
- Remove shebang lines from bin/ scripts for performance All scripts in bin/ are called through `pyenv` therefore the shebang lines is not necessary. On some systems this provides a measurable increase... — committed to cmcginty/pyenv-virtualenv by deleted user 6 years ago
- Remove shebang lines from bin/ scripts for performance All scripts in bin/ are called through `pyenv` therefore the shebang lines are not necessary. On some systems this provides a measurable increas... — committed to cmcginty/pyenv-virtualenv by deleted user 6 years ago
- Remove shebang lines from scripts for performance All scripts in libexec/ (excluding pyenv) are called through pyenv, therefore the shebang lines are not necessary. On some systems this provides a me... — committed to cmcginty/pyenv by deleted user 6 years ago
- Remove shebang lines from scripts for performance All scripts in libexec/ (excluding pyenv) are called through pyenv, therefore the shebang lines are not necessary. On some systems this provides a me... — committed to cmcginty/pyenv by deleted user 6 years ago
- Remove shebang lines from scripts for performance All scripts in libexec/ (excluding pyenv) are called through pyenv, therefore the shebang lines are not necessary. On some systems this provides a me... — committed to cmcginty/pyenv by deleted user 6 years ago
- Remove shebang lines from scripts for performance All scripts in libexec/ (excluding pyenv) are called through pyenv, therefore the shebang lines are not necessary. On some systems this provides a me... — committed to cmcginty/pyenv by deleted user 6 years ago
- Commented out pyenv virtualenv init This is slowing down shell commands. https://github.com/pyenv/pyenv-virtualenv/issues/259 — committed to mrlesmithjr/dotfiles by mrlesmithjr 4 years ago
- fix: try to fix slow zsh prompt Ref: https://github.com/pyenv/pyenv-virtualenv/issues/259 — committed to isvicy/dots by deleted user a year ago
On zsh replacing the precmd hook with a precwd hook seems like an ok workaround.
In my zshrc: eval “$(pyenv virtualenv-init - | sed s/precmd/precwd/g)”
then:
With precwd hook:
With precmd hook:
For me, it’s the
pyenv sh-activate --quiet
(called by_pyenv_virtualenv_hook
in myPROMPT_COMMAND
) that is taking up the bulk of the time.Thanks @monopoler08 your solution did not work for me, I did not have a
precwd
butchpwd
event. I changed your command with:And it worked perfectly.
Emphasis mine, just doing this blind obscures the problem, not solves it. The solution is to actually improve the performance of
pyenv-sh-activate
(or rather the hook, that currently calls it). Most of the negative performance appears to be caused by recursive call into the pyenv shell function / command to call other pyenv[-virtualenv] scripts as well as pyenv libexec utilities.So, for the sake of a fresh look at this investigative process:
If we can assume that we know what’s a libexec and what isn’t, and that we don’t have to depend on the idea of someone else overriding pyenv or this plugin’s behavior, a fix can be seen as follows:
The first check still takes 20-40 millis. Can we do any better? Littering the scripts with
date +%s%N | cut -b1-13 1>&2
(and timing this under zsh to take ~2.5ms, so, count of output x 2.5 => time spent on the date calls themselves) shows me this pattern:Which tells me that the actual prefix finding script isn’t a large deal. Digging deeper,
get_current_versions
is 40-60ms (incredibly variable), each call to the prefix versions script is ~ 20ms. Not much point in optimizing out the other call then. Can we do anything about get_current_versions?Logically speaking, the answer is yes-- it’s effectively the result of https://github.com/pyenv/pyenv/blob/5b4d5a32d343dcae5e7b3f1a09850312f89ba868/libexec/pyenv-version-name. The current versions (assuming no other plugins) can change under the result of the following commands:
pyenv shell
So, the ideal would be
.python_version
file if you have to. Which meansPYENV_VERSION
is set, skip callingget_current_versions
spawning subshells (that info is already in the env var).sh-activate
, cache-miss on changed directory (chpwd), set a separate hook such that every timepyenv global/local
are called, the versions cache gets cleared?I’ll try to implement this ideal over the next weekend (only focusing on zsh and by extension that the changes are not magic, bash , sorry) on a branch; and this should shave off the majority (>90ms/120ms per call) by my estimate.
Similar problem on Debian 9 with zsh. Removing
eval "$(pyenv virtualenv-init -)"
from~/.zshrc
fixes it.When using
virtualenv-init
in pyenv, by default,_pyenv_virtualenv_hook
runs every time a shell prompt appears. To avoid this, I tried to change to usechpwd
instead ofprecmd
hook. This improves the response speed of the shell because the_pyenv_virtualenv_hook
runs only upon directory changes.However, if this method was applied, the virtual environment was not activated immediately because the
_pyenv_virtualenv_hook
did not run when zsh was first run. As a workaround, I modified the hook with the sed command and added the code to run_pyenv_virtualenv_hook
once. This enabled the virtual environment to be activated normally at the start of zsh. I hope this information is helpful!More progress. I can reproduce outside of pyenv now. 😄 I wrote a shell script that creates and calls another script. If the script has a shebang line in it, then the execution is horrendous.
Even on my unaffected system, the performance is measurably worse when shebang line exists.
This worked for me on macos, however there is still the same delay when switching directories. I assume that this is due to the hook now only running when the current working dir is changed. Until there is a more efficient hook, every time it runs there seems to be a delay.
To find out what takes time, we need to get a debug trace of what’s running while you perform those actions and how long it takes.
For the first point
For the second point, using
$SECONDS
inPS4
may help. (I’m not sure what it does, but it seems to expand to the time since last shell prompt or invocation.) It that var doesn’t help, you’ll need to intersperse the code that runs when you reproduce the problem withtime
calls and use those to localize the time hog.Solution
Warning: the solution is far from perfect, it breaks command
pyenv activate/deactivate
. Use it only if the slowness really bothers you.Quick Solution
_pyenv_virtualenv_hook
pyenv activate/deactivate
, cause they’re broken without the above hook. Stick withpyenv shell env_name
,pyenv shell --unset
instead.For ZSH and Bash
Here’s an alternative
fish_preexec
runs before the command is processed. This moves the toil to before the command executes. This lets you type new commands without slowing you down@raphaelchristin instead of
chpwd
useprecwd
. I’m on macos and it works perfectly fine for me.For anyone using fish and looking for an equivalent to the zsh workaround, this seems to work for me:
Thank you for the updated instructions. I have used the following script to test the behavior:
which results in
The code snippet you’ve posted outputs an empty string. I have also reinstalled pyenv and the plugin, which has sped up things by a lot. If I just keep hitting enter the new line appears almost instantly. I have (probably?) had bad install or older version. It seems like pyenv wouldn’t update to its 2+ version using the built-in updater.