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

sourcing nvm.sh is slow even with --no-use #1978

Closed
Nuru opened this issue Jan 8, 2019 · 21 comments
Closed

sourcing nvm.sh is slow even with --no-use #1978

Nuru opened this issue Jan 8, 2019 · 21 comments
Labels
bugs Oh no, something's broken :-( OS: Mac OS performance This relates to anything regarding the speed of using nvm. shell: bash/sh

Comments

@Nuru
Copy link

Nuru commented Jan 8, 2019

I start up a lot of terminal windows on my Mac and for some reason the commands

  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" --no-use
  [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  

that I had in my .bashrc would sometimes take a noticeable and annoying amount of time to run. (Maybe as long as 2 seconds.) My solution was to setup environment variables like NVM_DIR as usual but to fully defer all other nvm setup until first use by adding these definitions to my .bash_profile script:

function _install_nvm() {
  unset -f nvm
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This sets up nvm
  [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # load nvm bash_completion
  nvm "$@"
}

function nvm() {
    _install_nvm "$@"
}

I am not familiar enough with other shells to say how portable that is (I am using GNU bash), but I suggest you at least add it to the documentation (if not the install script) so that people can have an easy way to install deferred setup of nvm that is truly deferred.

@ljharb
Copy link
Member

ljharb commented Jan 8, 2019

By default, this is because it runs npm config get prefix, which is quite slow. With --no-use, it's as fast as it can go, as far as I'm aware - and I've not heard of any speed complaints before with that.

As for your approach, that has a number of caveats - including that node, npm, and any global modules you have installed in a default version won't be available until you run nvm.

In other words, it would not be a good idea to add it to the documentation, because it's error-prone and comes with caveats.

@ljharb ljharb closed this as completed Jan 8, 2019
@Nuru
Copy link
Author

Nuru commented Jan 9, 2019

@ljharb I do not understand the relevance of the caveats you listed. I am not suggesting my solution to replace full initialization of nvm, only to replace/enhance the --no-use configuration. When using

. "$NVM_DIR/nvm.sh" --no-use

as currently recommended, node and npm remain unavailable until you execute nvm use (at least that's how it works for me), so your caveats apply equally to the current --no-use situation and the behavior with the deferred initialization.

With the script I suggested, nvm gets completely initialized the first time someone executes any nvm command. If you want, I could provide additional functions that initialize nvm on the first call to npm or node, but in my environment that is not helpful, because either way those commands do nothing until I nvm use some version of node.

@ljharb
Copy link
Member

ljharb commented Jan 10, 2019

That's a very fair point - but I'm still not sure why with --no-use it'd be slow - not one person has reported that previously.

I don't think the solution is to make nvm be lazy-loaded only when in "no-use" mode - i think a better solution is to figure out why sourcing nvm is slow on your machine, and fix that :-) I'll reopen for that.

(Note that nvm has many commands that do not require any version to be used or installed in order to be useful)

@ljharb ljharb reopened this Jan 10, 2019
@ljharb ljharb added the performance This relates to anything regarding the speed of using nvm. label Jan 10, 2019
@ljharb ljharb changed the title Faster startup for bashrc (Feature Request) sourcing nvm.sh is slow even with --no-use Jan 10, 2019
@ljharb ljharb added the needs followup We need some info or action from whoever filed this issue/PR. label Jan 10, 2019
@Nuru
Copy link
Author

Nuru commented Jan 10, 2019

@ljharb Thanks for re-opening this.

It appears --no-use is sometimes slow because nvm_supports_source_options is not reliable, at least not under GNU bash version 3.2.57(1) on my Mac, and when nvm_supports_source_options is false, the --no-use option is ignored.

$ (nvm_supports_source_options && echo true) || echo false
true
$ (nvm_supports_source_options && echo true) || echo false
false
$ i=0; while nvm_supports_source_options; do echo -n .; ((i++)); done; printf "\n%s\n" $i
..
2
$ i=0; while nvm_supports_source_options; do echo -n .; ((i++)); done; printf "\n%s\n" $i

0
$ i=0; while nvm_supports_source_options; do echo -n .; ((i++)); done; printf "\n%s\n" $i
.......
7

The current implementation of nvm_supports_source_options writes to a pipe that then tries to source that pipe as a shell script via /dev/stdin. I guess there is some race condition resulting in the output of the pipe being discarded rather than processed.

FWIW, I updated my .bash_profile script to this:

function _install_nvm() {
  unset -f nvm npm node
  # Set up "nvm" could use "--no-use" to defer setup, but we are here to use it
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This sets up nvm
  [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # nvm bash_completion
  "$@"
}

function nvm() {
    _install_nvm nvm "$@"
}

function npm() {
    _install_nvm npm "$@"
}

function node() {
    _install_nvm node "$@"
}

That suits me better, because there is no loading of anything until I need it, but when I need node, it is (almost) immediately available.

@ljharb
Copy link
Member

ljharb commented Jan 10, 2019

Interesting, thanks - that's helpful. I'll see about fixing nvm_supports_source_options so that it's reliable - I believe that would resolve your problem.

@Nuru
Copy link
Author

Nuru commented Jan 11, 2019

Looks like this is a bug in Gnu bash 3.2.x, which is what Apple ships, apparently because of licensing issues. (bash 3.2.57(1) is what shipped with El Capitan in 2015 and it is still what is shipping with Catalina in 2020, although starting with Catalina the default shell was switched to zsh.) It appears the receiving side of the pipe does not wait for the sending side of the pipe to start producing output before deciding that is has reached EOF. The best workaround I can give you so far is to use a here-is document instead of echo to produce the script. I'm not a portability expert and do not know what it might break, so I do not want to offer this as a PR, but feel free to take it.

nvm_supports_source_options() {
  [ "_$( . /dev/stdin yes 2> /dev/null <<'EOF'
[ $# -gt 0 ] && echo $1
EOF
       )" = "_yes" ]
}

@ljharb
Copy link
Member

ljharb commented Jan 11, 2019

That seems totally workable! I’ll test it out and report back.

@ljharb ljharb closed this as completed in 43402df Feb 27, 2019
@ljharb ljharb added shell: bash/sh bugs Oh no, something's broken :-( OS: Mac OS and removed needs followup We need some info or action from whoever filed this issue/PR. labels Feb 27, 2019
@tmpm697
Copy link

tmpm697 commented Jan 8, 2020

With this solution:

function _install_nvm() {
  unset -f nvm npm node
  # Set up "nvm" could use "--no-use" to defer setup, but we are here to use it
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This sets up nvm
  [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # nvm bash_completion
  "$@"
}

function nvm() {
    _install_nvm nvm "$@"
}

function npm() {
    _install_nvm npm "$@"
}

function node() {
    _install_nvm node "$@"
}

I still losing 0.01 - 0.03s of shell speed. but thanks anyway.

@milushov
Copy link

Spent a hour of debugging my .zshrc file, thanks god I found why

@damianobarbati
Copy link

@milushov what did you find out?

@insidewhy
Copy link

I use this for zsh:

if [ -s "$HOME/.nvm/nvm.sh" ]; then
  export NVM_DIR="$HOME/.nvm"
  nvm_cmds=(nvm node npm yarn)
  for cmd in $nvm_cmds ; do
    alias $cmd="unalias $nvm_cmds && unset nvm_cmds && . $NVM_DIR/nvm.sh && $cmd"
  done
fi

@anandncode
Copy link

anandncode commented Dec 21, 2020

I am using zsh 5.8 (x86_64-apple-darwin20.1.0) in my Mac (MacOS Big Sur version 11.0.1 (20B29))

Even when i use --no-use, the start up time is horrible. Every new tab open has become so slow. When I ran some profiling using zprof, below are the statistics

num  calls                time                       self            name
-----------------------------------------------------------------------------------
 1)    1        5102.49  5102.49   92.18%   5102.49  5102.49   92.18%  nvm_supports_source_options
 2)    2         148.97    74.49    2.69%    124.40    62.20    2.25%  compinit

As we can see nvm_supports_source_options is taking hell lot of time.

nvm_supports_source_options() {
  # shellcheck disable=SC1091,SC2240
    [ "_$( . /dev/stdin yes 2> /dev/null <<'EOF'
[ $# -gt 0 ] && nvm_echo $1
EOF
  )" = "_yes" ]
}

As a result --no-use is of also no help to me. So I believe the issue is back again with Big Sur :(
Is there anything that I can do to fix?

Should I go ahead with #1978 (comment)

@ljharb
Copy link
Member

ljharb commented Dec 21, 2020

@AnandNidamanuru what version of nvm? v0.37.0 introduced a major performance improvement. Can you try on the latest version?

@anandncode
Copy link

My nvm version is 0.37.2 :(
The first thing I did was to upgrade nvm. Yet it is the same...

I believe the issue is with source to /dev/stdin. But I am no expert in shell

@ljharb
Copy link
Member

ljharb commented Dec 21, 2020

@AnandNidamanuru would you mind filing a new issue, and filling out the issue template in its entirety?

@anandncode
Copy link

Thanks @ljharb for your quick replies. I will do that

@anandncode
Copy link

@ljharb Here is the new issue
#2387

@martin-braun
Copy link

@ljharb love your work, but how is using --no-use solving this issue? I want a fast loading terminal without lazy loading tricks and with the use auto-magic available? I think it would be better to cache the output of npm config get prefix and provide a command to purge the cache.

Would this be possible?

@ljharb
Copy link
Member

ljharb commented Sep 22, 2022

No, because then that would be incorrect, since the prefix can change based on the current directory as well as the value of env vars.

However, that particular slowness has been resolved since this was closed. If you’re still having performance problems in the latest version, please file a new issue.

@martin-braun
Copy link

@ljharb Yes, the slowness has been resolved when using --no-use. The above mentioned work-arounds with lazy-load introduce compatibility problems, i.e. scripts that rely on npm will fail. Tools like n don't produce those slow encounters, but I was attempting to switch to nvm because of its auto-magic.

@ljharb
Copy link
Member

ljharb commented Sep 23, 2022

@martin-braun i meant that the slowness of npm config get prefix is resolved.

If you're still finding it necessary to use --no-use on the latest version of nvm, please file a new issue with a filled-out issue template.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugs Oh no, something's broken :-( OS: Mac OS performance This relates to anything regarding the speed of using nvm. shell: bash/sh
Projects
None yet
Development

No branches or pull requests

8 participants