nvm: nvm.sh is slow (200ms+)

On my system (OSX 10.9.5 & bash 4.3.27), nvm adds a lot of time to my .bash_profile:

$ time source ~/.nvm/nvm.sh

real    0m0.218s
user    0m0.107s
sys 0m0.163s

–This is much slower than rvm–:

$ time source ~/.rvm/scripts/rvm

real    0m0.011s
user    0m0.005s
sys 0m0.006s

I do not use .nvmrc files.

About this issue

  • Original URL
  • State: closed
  • Created 10 years ago
  • Reactions: 5
  • Comments: 51 (21 by maintainers)

Commits related to this issue

Most upvoted comments

For anyone who comes across this in the future;

nvm kills my productivity because bash takes two orders of magnitude longer to create a new shell when a default alias exists. As a workaround, I added this hack to my .bash_profile after nvm.sh is loaded:

# nvm
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" --no-use # This loads nvm

alias node='unalias node ; unalias npm ; nvm use default ; node $@'
alias npm='unalias node ; unalias npm ; nvm use default ; npm $@'

Delete the default alias and .nvmrc; Shell creation is fast again, and I still have a default node through the alias hack. First run of node or npm has the startup penalty, but that’s a far better compromise than typing nvm use v4.4.7 every time I want to start a REPL.

Maybe need use zsh-async? Works very fast

# Install zsh-async if it’s not present
if [[ ! -a ~/.zsh-async ]]; then
  git clone git@github.com:mafredri/zsh-async.git ~/.zsh-async
fi
source ~/.zsh-async/async.zsh

export NVM_DIR="$HOME/.nvm"
function load_nvm() {
    [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
    [ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"
}

# Initialize worker
async_start_worker nvm_worker -n
async_register_callback nvm_worker load_nvm
async_job nvm_worker sleep 0.1

In zsh I use lazy loading for source files. This is not a solution but shell runs faster.

lazy_source () {
    eval "$1 () { [ -f $2 ] && source $2 && $1 \$@ }"
}

NVM_SOURCE=$HOME/.nvm/nvm.sh
lazy_source nvm $NVM_SOURCE

the --no-use solves the problem indeed, thanks

When I have either a default alias, or an .nvmrc file, or both, it’s slow. When I have neither, it’s fast.

I think the best solution is to add --no-use to your profile file - ie, . "$NVM_DIR/nvm.sh" --no-use. Does that solve the problem for whoever’s subscribed to this issue?

That wouldn’t work for all the other non-zsh shells that nvm supports.

For people who want to respect the .nvmrc without thinking, and still preserve the lazy loading so that shell boot and cd are still fast in zsh, I managed to put together a nice alias method off of @parasyte’s method.

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" --no-use # This setups nvm to be lazy-loaded

load-nvmrc() {
  local node_version="$(nvm version)"
  local nvmrc_path="$(nvm_find_nvmrc)"

  if [ -n "$nvmrc_path" ]; then
    local nvmrc_node_version=$(nvm version "$(cat "${nvmrc_path}")")

    if [ "$nvmrc_node_version" = "N/A" ]; then
      nvm install
    elif [ "$nvmrc_node_version" != "$node_version" ]; then
      nvm use
    fi
  elif [ "$node_version" != "$(nvm version default)" ]; then
    echo "Reverting to nvm default version"
    nvm use default
  fi
}

# Alias node, npm, and yarn to access node lazily. We reset the alias on cd to ensure we always use
# the right nvm version when in a new folder (respecting the nvmrc if possible).
alias node='unalias node ; unalias npm ; unalias yarn ; export NVM_LOADED_FOR_PATH=1 ; load-nvmrc ; node $@';
alias npm='unalias node ; unalias npm ; unalias yarn ; export NVM_LOADED_FOR_PATH=1 ; load-nvmrc ; npm $@';
alias yarn='unalias node ; unalias npm ; unalias yarn ; export NVM_LOADED_FOR_PATH=1 ; load-nvmrc ; yarn $@';

unset_nvm() {
  if [[ "$NVM_LOADED_FOR_PATH" -eq 1 ]]; then
    export NVM_LOADED_FOR_PATH=0
    alias node='unalias node ; unalias npm ; unalias yarn ; export NVM_LOADED_FOR_PATH=1 ; load-nvmrc ; node $@';
    alias npm='unalias node ; unalias npm ; unalias yarn ; export NVM_LOADED_FOR_PATH=1 ; load-nvmrc ; npm $@';
    alias yarn='unalias node ; unalias npm ; unalias yarn ; export NVM_LOADED_FOR_PATH=1 ; load-nvmrc ; yarn $@';
  fi
}

add-zsh-hook chpwd unset_nvm

The nice thing is that this allows the system node and yarn to still work (as other home-brew dependencies often install them). It works by resetting the alias on cdso that the next time you run, it will check to make sure your version is correct (rather than running on cd, which is slow and too optimistic).

@belozer solution is by far the fastest one on my machine

How about this?

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" --no-use  # This loads nvm
export PATH=$HOME/.nvm/versions/node/v$(cat $HOME/.nvm/alias/default)/bin:$PATH

What could be missing with this code?

Gotcha. That is a nice little enhancement. I misunderstood that you were providing a tip. 😃

I guess I’ll have to keep the alias, and stay hopeful that one day the startup time will get better! Cheers.

@belozer - that is what I needed, after a lot of searching. I was using a zsh nvm plugin (tried a couple of different ones), but your solution is the fastest on OSX & Linux.

For that and other reasons, I gave up and switched to managing node with brew. It’s ugly, but a few aliases will take care of that, surely.

jay@JayMBP:~$ readlink $(which node)
../Cellar/node@6/6.11.3/bin/node
jay@JayMBP:~$ node --version
v6.11.3

jay@JayMBP:~$ brew unlink node@6 && brew link --force node@4
Unlinking /usr/local/Cellar/node@6/6.11.3... 7 symlinks removed
Linking /usr/local/Cellar/node@4/4.8.4_1... 7 symlinks created

If you need to have this software first in your PATH instead consider running:
  echo 'export PATH="/usr/local/opt/node@4/bin:$PATH"' >> ~/.bash_profile
jay@JayMBP:~$ readlink $(which node)
../Cellar/node@4/4.8.4_1/bin/node
jay@JayMBP:~$ node --version
v4.8.4

jay@JayMBP:~$ brew unlink node@4 && brew link --force node
Unlinking /usr/local/Cellar/node@4/4.8.4_1... 7 symlinks removed
Linking /usr/local/Cellar/node/8.6.0... 7 symlinks created
jay@JayMBP:~$ readlink $(which node)
../Cellar/node/8.6.0/bin/node
jay@JayMBP:~$ node --version
v8.6.0

jay@JayMBP:~$ brew unlink node && brew link --force node@6
Unlinking /usr/local/Cellar/node/8.6.0... 7 symlinks removed
Linking /usr/local/Cellar/node@6/6.11.3... 7 symlinks created

If you need to have this software first in your PATH instead consider running:
  echo 'export PATH="/usr/local/opt/node@6/bin:$PATH"' >> ~/.bash_profile
jay@JayMBP:~$ readlink $(which node)
../Cellar/node@6/6.11.3/bin/node
jay@JayMBP:~$ node --version
v6.11.3

@medisoft If you want to be closer to nvm’s internal code, you could use:

export PATH="$(nvm_prepend_path "$PATH" "$(nvm_version_path "$(nvm_alias default)")/bin")"

That still fails to set the MANPATH but that’s pretty close.

@ljharb

$ nvm unalias default
Deleted alias default - restore it with `nvm alias "default" "node"`
$ time source ~/.nvm/nvm.sh

real    0m0.123s
user    0m0.052s
sys     0m0.048s