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

Performance: `nvm use` takes about a second #860

Open
dylanpyle opened this Issue Oct 6, 2015 · 71 comments

Comments

Projects
None yet
@dylanpyle

dylanpyle commented Oct 6, 2015

(Pulling some details out of the thread on #703 per request from @ljharb)

nvm use is slow. This makes shell startup slow, and switching versions after shell startup slow too.

$ time (nvm use 0.10)
Now using node v0.10.38 (npm v1.4.28)
( nvm use 0.10; )  0.50s user 0.20s system 83% cpu 0.846 total

$ time (nvm use 4)
Now using node v4.1.2 (npm v2.14.4)
( nvm use 4; )  0.72s user 0.23s system 86% cpu 1.089 total

System information:

  • nvm 0.28.0
  • zsh 5.0.8 (but same issue appears in bash/sh)
  • osx 10.10.5
$ nvm debug
$SHELL: /bin/zsh
$NVM_DIR: '$HOME/.nvm'
$PREFIX: ''
$NPM_CONFIG_PREFIX: ''
nvm current: v0.10.38
which node: $NVM_DIR/v0.10.38/bin/node
which iojs: iojs not found
which npm: $NVM_DIR/v0.10.38/bin/npm
npm config get prefix: $NVM_DIR/v0.10.38
npm root -g: $NVM_DIR/v0.10.38/lib/node_modules

My temporary solution is as follows:

$ nvm unalias default

— then adding this hack in my zshrc

@behrangsa

This comment has been minimized.

Show comment
Hide comment
@behrangsa

behrangsa Oct 10, 2015

It's very slow for me too. My SSD is a few years old so it is not the latest and greatest, but I guess nvm should not make execution of my bash ~/.profile script this slow. It adds an extra 1.2 seconds delay to my Teminal whenever I open a new tab:

export NVM_DIR=~/.nvm
time source $(brew --prefix nvm)/nvm.sh

This outputs:

real    0m1.175s
user    0m0.863s
sys 0m0.256s
$ nvm debug
$SHELL: /bin/bash
$NVM_DIR: $HOME/.nvm
nvm current: v4.1.1
which node: $NVM_DIR/versions/node/v4.1.1/bin/node
which iojs: 
which npm: $NVM_DIR/versions/node/v4.1.1/bin/npm
npm config get prefix: $NVM_DIR/versions/node/v4.1.1
npm root -g: $NVM_DIR/versions/node/v4.1.1/lib/node_modules

behrangsa commented Oct 10, 2015

It's very slow for me too. My SSD is a few years old so it is not the latest and greatest, but I guess nvm should not make execution of my bash ~/.profile script this slow. It adds an extra 1.2 seconds delay to my Teminal whenever I open a new tab:

export NVM_DIR=~/.nvm
time source $(brew --prefix nvm)/nvm.sh

This outputs:

real    0m1.175s
user    0m0.863s
sys 0m0.256s
$ nvm debug
$SHELL: /bin/bash
$NVM_DIR: $HOME/.nvm
nvm current: v4.1.1
which node: $NVM_DIR/versions/node/v4.1.1/bin/node
which iojs: 
which npm: $NVM_DIR/versions/node/v4.1.1/bin/npm
npm config get prefix: $NVM_DIR/versions/node/v4.1.1
npm root -g: $NVM_DIR/versions/node/v4.1.1/lib/node_modules
@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Oct 10, 2015

Collaborator

@behrangsa nvm with homebrew is not supported. can you try installing properly, with the curl command in the readme, and see if it's any different? (note that brew --prefix nvm may be taking some time too)

Collaborator

ljharb commented Oct 10, 2015

@behrangsa nvm with homebrew is not supported. can you try installing properly, with the curl command in the readme, and see if it's any different? (note that brew --prefix nvm may be taking some time too)

@behrangsa

This comment has been minimized.

Show comment
Hide comment
@behrangsa

behrangsa Oct 10, 2015

@ljharb Hi Jordan,

I hardcoded brew --prefix nvm to /usr/local/opt/nvm and it made loading nvm a bit faster, but it is still slower than one would expect IMHO:

First load:

real    0m1.380s
user    0m0.883s
sys 0m0.276s

Subsequent loads (in new tabs):

real    0m0.696s
user    0m0.582s
sys 0m0.144s

So I guess this is more or less inline with @dylanpyle's findings, even though that I've installed nvm via brew.

behrangsa commented Oct 10, 2015

@ljharb Hi Jordan,

I hardcoded brew --prefix nvm to /usr/local/opt/nvm and it made loading nvm a bit faster, but it is still slower than one would expect IMHO:

First load:

real    0m1.380s
user    0m0.883s
sys 0m0.276s

Subsequent loads (in new tabs):

real    0m0.696s
user    0m0.582s
sys 0m0.144s

So I guess this is more or less inline with @dylanpyle's findings, even though that I've installed nvm via brew.

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Oct 10, 2015

Collaborator

Thanks, I appreciate that - I doubt installing with brew affects the speed of nvm use, so your timing info is helpful. What's nvm --version say btw? (I just added this in nvm debug in c957989)

Collaborator

ljharb commented Oct 10, 2015

Thanks, I appreciate that - I doubt installing with brew affects the speed of nvm use, so your timing info is helpful. What's nvm --version say btw? (I just added this in nvm debug in c957989)

@behrangsa

This comment has been minimized.

Show comment
Hide comment
@behrangsa

behrangsa Oct 10, 2015

It is 0.27.1.

behrangsa commented Oct 10, 2015

It is 0.27.1.

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Oct 10, 2015

Collaborator

@behrangsa can you try with 0.29.0 and see if it's any better?

Collaborator

ljharb commented Oct 10, 2015

@behrangsa can you try with 0.29.0 and see if it's any better?

@behrangsa

This comment has been minimized.

Show comment
Hide comment
@behrangsa

behrangsa Oct 10, 2015

I just upgraded to 0.29.0 and the performance is almost the same:

$ nvm --version
0.29.0
real  0m0.653s
user  0m0.542s
sys   0m0.137s

behrangsa commented Oct 10, 2015

I just upgraded to 0.29.0 and the performance is almost the same:

$ nvm --version
0.29.0
real  0m0.653s
user  0m0.542s
sys   0m0.137s
@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Oct 10, 2015

Collaborator

thanks - i'll keep looking into it

Collaborator

ljharb commented Oct 10, 2015

thanks - i'll keep looking into it

@behrangsa

This comment has been minimized.

Show comment
Hide comment
@behrangsa

behrangsa commented Oct 10, 2015

Thanks @ljharb! 👍

@dylanpyle

This comment has been minimized.

Show comment
Hide comment
@dylanpyle

dylanpyle Oct 12, 2015

More data: I'm seeing the same issue on a different machine with a similar setup:

  • nvm 0.29.0
  • osx 10.11
  • zsh 5.0.6

Issue is present even with everything non-nvm related removed from zshrc - hopefully ruling out conflicting scripts.

dylanpyle commented Oct 12, 2015

More data: I'm seeing the same issue on a different machine with a similar setup:

  • nvm 0.29.0
  • osx 10.11
  • zsh 5.0.6

Issue is present even with everything non-nvm related removed from zshrc - hopefully ruling out conflicting scripts.

ryanwilsonperkin added a commit to ryanwilsonperkin/dotfiles that referenced this issue Oct 13, 2015

Add nvm
Until creationix/nvm#860 is solved and nvm.sh
is optimized for speed, this will only export the script path, invoking
it must be done explicitly.

I use this workflow by opening a new shell, and running
`source $NVM_SCRIPT`.
@sebicas

This comment has been minimized.

Show comment
Hide comment
@sebicas

sebicas Oct 23, 2015

Same problem here:

  • nvm 0.28.0
  • osx 10.10.5

sebicas commented Oct 23, 2015

Same problem here:

  • nvm 0.28.0
  • osx 10.10.5
@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Oct 23, 2015

Collaborator

@sebicas what's the output of nvm debug? What shell are you using?

Collaborator

ljharb commented Oct 23, 2015

@sebicas what's the output of nvm debug? What shell are you using?

@sebicas

This comment has been minimized.

Show comment
Hide comment
@sebicas

sebicas Oct 23, 2015

@ljharb here is the output

$ nvm debug
$SHELL: /bin/bash
$NVM_DIR: '$HOME/.nvm'
$PREFIX: ''
$NPM_CONFIG_PREFIX: ''
nvm current: v4.2.1
which node: $NVM_DIR/versions/node/v4.2.1/bin/node
which iojs: 
which npm: $NVM_DIR/versions/node/v4.2.1/bin/npm
npm config get prefix: $NVM_DIR/versions/node/v4.2.1
npm root -g: $NVM_DIR/versions/node/v4.2.1/lib/node_modules

I am using the default osx shell

sebicas commented Oct 23, 2015

@ljharb here is the output

$ nvm debug
$SHELL: /bin/bash
$NVM_DIR: '$HOME/.nvm'
$PREFIX: ''
$NPM_CONFIG_PREFIX: ''
nvm current: v4.2.1
which node: $NVM_DIR/versions/node/v4.2.1/bin/node
which iojs: 
which npm: $NVM_DIR/versions/node/v4.2.1/bin/npm
npm config get prefix: $NVM_DIR/versions/node/v4.2.1
npm root -g: $NVM_DIR/versions/node/v4.2.1/lib/node_modules

I am using the default osx shell

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Oct 23, 2015

Collaborator

Thanks - by chance do you have an SSD, or a standard HDD?

Collaborator

ljharb commented Oct 23, 2015

Thanks - by chance do you have an SSD, or a standard HDD?

@sebicas

This comment has been minimized.

Show comment
Hide comment
@sebicas

sebicas Oct 23, 2015

Standard HDD is a Western Digital Scorpio Blue (750GB WD7500BPVT) just in case.

sebicas commented Oct 23, 2015

Standard HDD is a Western Digital Scorpio Blue (750GB WD7500BPVT) just in case.

@jcpst

This comment has been minimized.

Show comment
Hide comment
@jcpst

jcpst Oct 29, 2015

Hi, noticing the same problem here. I isolated the lag in my shell loading down to the sourcing of the nvm script.

  • nvm 0.29.0
  • Arch Linux
  • Zsh
  • SSD

jcpst commented Oct 29, 2015

Hi, noticing the same problem here. I isolated the lag in my shell loading down to the sourcing of the nvm script.

  • nvm 0.29.0
  • Arch Linux
  • Zsh
  • SSD
@bittner

This comment has been minimized.

Show comment
Hide comment
@bittner

bittner Nov 1, 2015

I have "solved" this issue for me the following, pragmatic way in ~/.bashrc: (Ubuntu 14.04.3 LTS)

alias node='unalias node npm && nvm use v4.2.1 && node'
alias npm='node -v > /dev/null && npm'  # optional

This way my shell always starts up fast, and until I need node for the first time there is no delay.

With the second line also npm is supported in the same fashion. (Here, it's actually important that the unalias happens before nvm use is called. I guess, due to a reference to npm in the setup of node.)

bittner commented Nov 1, 2015

I have "solved" this issue for me the following, pragmatic way in ~/.bashrc: (Ubuntu 14.04.3 LTS)

alias node='unalias node npm && nvm use v4.2.1 && node'
alias npm='node -v > /dev/null && npm'  # optional

This way my shell always starts up fast, and until I need node for the first time there is no delay.

With the second line also npm is supported in the same fashion. (Here, it's actually important that the unalias happens before nvm use is called. I guess, due to a reference to npm in the setup of node.)

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Nov 1, 2015

Collaborator

Note that that workaround will mean all of your globally-installed modules will be unavailable until after you've referenced node.

Collaborator

ljharb commented Nov 1, 2015

Note that that workaround will mean all of your globally-installed modules will be unavailable until after you've referenced node.

@decached

This comment has been minimized.

Show comment
Hide comment
@decached

decached Nov 6, 2015

+1

I have the same problem. I use prezto and I don't think the shell or the shell-manager is causing problem.

  • nvm 0.29.0
  • zsh 5.1.1
  • Ubuntu 15.10

Prezto zsh without nvm

$ time /bin/zsh -i -c exit
/bin/zsh -i -c exit  0.14s user 0.07s system 92% cpu 0.230 total

Prezto zsh with nvm

$ time /bin/zsh -i -c exit
/bin/zsh -i -c exit  0.57s user 0.10s system 86% cpu 0.781 total

Vanilla zsh without nvm

$ time /bin/zsh -i -c exit
/bin/zsh -i -c exit  0.01s user 0.00s system 74% cpu 0.016 total

Vanilla zsh with nvm

$ time /bin/zsh -i -c exit
/bin/zsh -i -c exit  0.52s user 0.08s system 88% cpu 0.676 total

nvm.sh seems to be taking 0.5s on average to load up.

decached commented Nov 6, 2015

+1

I have the same problem. I use prezto and I don't think the shell or the shell-manager is causing problem.

  • nvm 0.29.0
  • zsh 5.1.1
  • Ubuntu 15.10

Prezto zsh without nvm

$ time /bin/zsh -i -c exit
/bin/zsh -i -c exit  0.14s user 0.07s system 92% cpu 0.230 total

Prezto zsh with nvm

$ time /bin/zsh -i -c exit
/bin/zsh -i -c exit  0.57s user 0.10s system 86% cpu 0.781 total

Vanilla zsh without nvm

$ time /bin/zsh -i -c exit
/bin/zsh -i -c exit  0.01s user 0.00s system 74% cpu 0.016 total

Vanilla zsh with nvm

$ time /bin/zsh -i -c exit
/bin/zsh -i -c exit  0.52s user 0.08s system 88% cpu 0.676 total

nvm.sh seems to be taking 0.5s on average to load up.

@ibash

This comment has been minimized.

Show comment
Hide comment
@ibash

ibash Nov 19, 2015

Anecdote:

I tracked down my slow shell to nvm as well. I found the following things slow:

In my .bash_profile I had:

export NVM_DIR=~/.nvm
source `brew --prefix nvm`/nvm.sh

Findings:

  • brew --prefix nvm was very slow
  • When I commented out export NVM_DIR=~/.nvm I found a huge speed improvement

My two solutions were:

  • Change brew --prefix nvm to the absolute path
  • I blew away ~/.nvm and reinstalled the versions of node I needed. I suspect that it had something to do with versions of node installed with an older version of nvm.

ibash commented Nov 19, 2015

Anecdote:

I tracked down my slow shell to nvm as well. I found the following things slow:

In my .bash_profile I had:

export NVM_DIR=~/.nvm
source `brew --prefix nvm`/nvm.sh

Findings:

  • brew --prefix nvm was very slow
  • When I commented out export NVM_DIR=~/.nvm I found a huge speed improvement

My two solutions were:

  • Change brew --prefix nvm to the absolute path
  • I blew away ~/.nvm and reinstalled the versions of node I needed. I suspect that it had something to do with versions of node installed with an older version of nvm.
@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Nov 19, 2015

Collaborator

@ibash nvm is utterly unsupported on homebrew. please don't install it that way, and use the curl command in the readme. nvm doesn't install node versions any differently than it used to, so I don't think that's the problem.

Collaborator

ljharb commented Nov 19, 2015

@ibash nvm is utterly unsupported on homebrew. please don't install it that way, and use the curl command in the readme. nvm doesn't install node versions any differently than it used to, so I don't think that's the problem.

@ibash

This comment has been minimized.

Show comment
Hide comment
@ibash

ibash Nov 19, 2015

Just reinstalled w/o homebrew. You're right, I think what I was seeing was a side effect of blowing away my alias default.

ibash commented Nov 19, 2015

Just reinstalled w/o homebrew. You're right, I think what I was seeing was a side effect of blowing away my alias default.

@kilianc

This comment has been minimized.

Show comment
Hide comment
@kilianc

kilianc Dec 7, 2015

Contributor

Same over here

$ source ~/.nvm/nvm.sh

takes ~0.7s to execute

edit

sourcing is fine if I nvm unalias default the bottleneck is nvm use

Contributor

kilianc commented Dec 7, 2015

Same over here

$ source ~/.nvm/nvm.sh

takes ~0.7s to execute

edit

sourcing is fine if I nvm unalias default the bottleneck is nvm use

@kilianc

This comment has been minimized.

Show comment
Hide comment
@kilianc

kilianc Dec 7, 2015

Contributor

I commented out part of the script for nvm use and my shell now loads instantly, see https://github.com/creationix/nvm/blob/master/nvm.sh#L1806

diff --git a/nvm.sh b/nvm.sh
index c6b8f83..0ad90f1 100755
--- a/nvm.sh
+++ b/nvm.sh
@@ -1805,11 +1805,11 @@ nvm() {

       # This nvm_ensure_version_installed call can be a performance bottleneck
       # on shell startup. Perhaps we can optimize it away or make it faster.
-      nvm_ensure_version_installed "$PROVIDED_VERSION"
-      EXIT_CODE=$?
-      if [ "$EXIT_CODE" != "0" ]; then
-        return $EXIT_CODE
-      fi
+      # nvm_ensure_version_installed "$PROVIDED_VERSION"
+      # EXIT_CODE=$?
+      # if [ "$EXIT_CODE" != "0" ]; then
+      #   return $EXIT_CODE
+      # fi

       local NVM_VERSION_DIR
       NVM_VERSION_DIR="$(nvm_version_path "$VERSION")"
@@ -1836,28 +1836,28 @@ nvm() {
         command rm -f "$NVM_DIR/current" && ln -s "$NVM_VERSION_DIR" "$NVM_DIR/current"
       fi
       local NVM_USE_OUTPUT
-      if nvm_is_iojs_version "$VERSION"; then
-        if [ $NVM_USE_SILENT -ne 1 ]; then
-          NVM_USE_OUTPUT="Now using io.js $(nvm_strip_iojs_prefix "$VERSION")$(nvm_print_npm_version)"
-        fi
-      else
-        if [ $NVM_USE_SILENT -ne 1 ]; then
-          NVM_USE_OUTPUT="Now using node $VERSION$(nvm_print_npm_version)"
-        fi
-      fi
-      if [ "_$VERSION" != "_system" ]; then
-        local NVM_USE_CMD
-        NVM_USE_CMD="nvm use --delete-prefix"
-        if [ -n "$PROVIDED_VERSION" ]; then
-          NVM_USE_CMD="$NVM_USE_CMD $VERSION"
-        fi
-        if [ $NVM_USE_SILENT -eq 1 ]; then
-          NVM_USE_CMD="$NVM_USE_CMD --silent"
-        fi
-        if ! nvm_die_on_prefix "$NVM_DELETE_PREFIX" "$NVM_USE_CMD"; then
-          return 11
-        fi
-      fi
+      # if nvm_is_iojs_version "$VERSION"; then
+      #   if [ $NVM_USE_SILENT -ne 1 ]; then
+      #     NVM_USE_OUTPUT="Now using io.js $(nvm_strip_iojs_prefix "$VERSION")$(nvm_print_npm_version)"
+      #   fi
+      # else
+      #   if [ $NVM_USE_SILENT -ne 1 ]; then
+      #     NVM_USE_OUTPUT="Now using node $VERSION$(nvm_print_npm_version)"
+      #   fi
+      # fi
+      # if [ "_$VERSION" != "_system" ]; then
+      #   local NVM_USE_CMD
+      #   NVM_USE_CMD="nvm use --delete-prefix"
+      #   if [ -n "$PROVIDED_VERSION" ]; then
+      #     NVM_USE_CMD="$NVM_USE_CMD $VERSION"
+      #   fi
+      #   if [ $NVM_USE_SILENT -eq 1 ]; then
+      #     NVM_USE_CMD="$NVM_USE_CMD --silent"
+      #   fi
+      #   if ! nvm_die_on_prefix "$NVM_DELETE_PREFIX" "$NVM_USE_CMD"; then
+      #     return 11
+      #   fi
+      # fi
       if [ -n "$NVM_USE_OUTPUT" ]; then
         echo "$NVM_USE_OUTPUT"
       fi
Contributor

kilianc commented Dec 7, 2015

I commented out part of the script for nvm use and my shell now loads instantly, see https://github.com/creationix/nvm/blob/master/nvm.sh#L1806

diff --git a/nvm.sh b/nvm.sh
index c6b8f83..0ad90f1 100755
--- a/nvm.sh
+++ b/nvm.sh
@@ -1805,11 +1805,11 @@ nvm() {

       # This nvm_ensure_version_installed call can be a performance bottleneck
       # on shell startup. Perhaps we can optimize it away or make it faster.
-      nvm_ensure_version_installed "$PROVIDED_VERSION"
-      EXIT_CODE=$?
-      if [ "$EXIT_CODE" != "0" ]; then
-        return $EXIT_CODE
-      fi
+      # nvm_ensure_version_installed "$PROVIDED_VERSION"
+      # EXIT_CODE=$?
+      # if [ "$EXIT_CODE" != "0" ]; then
+      #   return $EXIT_CODE
+      # fi

       local NVM_VERSION_DIR
       NVM_VERSION_DIR="$(nvm_version_path "$VERSION")"
@@ -1836,28 +1836,28 @@ nvm() {
         command rm -f "$NVM_DIR/current" && ln -s "$NVM_VERSION_DIR" "$NVM_DIR/current"
       fi
       local NVM_USE_OUTPUT
-      if nvm_is_iojs_version "$VERSION"; then
-        if [ $NVM_USE_SILENT -ne 1 ]; then
-          NVM_USE_OUTPUT="Now using io.js $(nvm_strip_iojs_prefix "$VERSION")$(nvm_print_npm_version)"
-        fi
-      else
-        if [ $NVM_USE_SILENT -ne 1 ]; then
-          NVM_USE_OUTPUT="Now using node $VERSION$(nvm_print_npm_version)"
-        fi
-      fi
-      if [ "_$VERSION" != "_system" ]; then
-        local NVM_USE_CMD
-        NVM_USE_CMD="nvm use --delete-prefix"
-        if [ -n "$PROVIDED_VERSION" ]; then
-          NVM_USE_CMD="$NVM_USE_CMD $VERSION"
-        fi
-        if [ $NVM_USE_SILENT -eq 1 ]; then
-          NVM_USE_CMD="$NVM_USE_CMD --silent"
-        fi
-        if ! nvm_die_on_prefix "$NVM_DELETE_PREFIX" "$NVM_USE_CMD"; then
-          return 11
-        fi
-      fi
+      # if nvm_is_iojs_version "$VERSION"; then
+      #   if [ $NVM_USE_SILENT -ne 1 ]; then
+      #     NVM_USE_OUTPUT="Now using io.js $(nvm_strip_iojs_prefix "$VERSION")$(nvm_print_npm_version)"
+      #   fi
+      # else
+      #   if [ $NVM_USE_SILENT -ne 1 ]; then
+      #     NVM_USE_OUTPUT="Now using node $VERSION$(nvm_print_npm_version)"
+      #   fi
+      # fi
+      # if [ "_$VERSION" != "_system" ]; then
+      #   local NVM_USE_CMD
+      #   NVM_USE_CMD="nvm use --delete-prefix"
+      #   if [ -n "$PROVIDED_VERSION" ]; then
+      #     NVM_USE_CMD="$NVM_USE_CMD $VERSION"
+      #   fi
+      #   if [ $NVM_USE_SILENT -eq 1 ]; then
+      #     NVM_USE_CMD="$NVM_USE_CMD --silent"
+      #   fi
+      #   if ! nvm_die_on_prefix "$NVM_DELETE_PREFIX" "$NVM_USE_CMD"; then
+      #     return 11
+      #   fi
+      # fi
       if [ -n "$NVM_USE_OUTPUT" ]; then
         echo "$NVM_USE_OUTPUT"
       fi
@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Dec 7, 2015

Collaborator

@kilianc Thanks - it's likely mostly the nvm_die_on_prefix command. if you comment out just those three lines, how fast does it load?

Collaborator

ljharb commented Dec 7, 2015

@kilianc Thanks - it's likely mostly the nvm_die_on_prefix command. if you comment out just those three lines, how fast does it load?

@kilianc

This comment has been minimized.

Show comment
Hide comment
@kilianc

kilianc Dec 7, 2015

Contributor

@ljharb I tried various combinations but everytime I uncomment something it adds between 100 to 300ms.

This is pretty slow $(nvm_print_npm_version) also

Contributor

kilianc commented Dec 7, 2015

@ljharb I tried various combinations but everytime I uncomment something it adds between 100 to 300ms.

This is pretty slow $(nvm_print_npm_version) also

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Dec 7, 2015

Collaborator

Yes, invoking npm seems to add a bunch of time. I could easily remove printing the npm version, but nvm_die_on_prefix is critically important to run.

Collaborator

ljharb commented Dec 7, 2015

Yes, invoking npm seems to add a bunch of time. I could easily remove printing the npm version, but nvm_die_on_prefix is critically important to run.

@kilianc

This comment has been minimized.

Show comment
Hide comment
@kilianc

kilianc Dec 7, 2015

Contributor

@ljharb I know, but from my perspective I am not going to uncomment those line unless it turns out I broke something :D it's 1s vs 30ms

Contributor

kilianc commented Dec 7, 2015

@ljharb I know, but from my perspective I am not going to uncomment those line unless it turns out I broke something :D it's 1s vs 30ms

@Rush

This comment has been minimized.

Show comment
Hide comment
@Rush

Rush Jun 9, 2016

sourcing nvm.sh is what sets up the PATH. If it could be done more performantly, nvm would be doing it.

Of course it could but the only way to cut down the time is to do less. Can nvm do it? Clearly it hasn't happened yet so maybe it's not that simple. I am saying to cache the PATH in some small env file that will assure only minimal amount of code is run. Why optimize existing code and risk breaking?

Rush commented Jun 9, 2016

sourcing nvm.sh is what sets up the PATH. If it could be done more performantly, nvm would be doing it.

Of course it could but the only way to cut down the time is to do less. Can nvm do it? Clearly it hasn't happened yet so maybe it's not that simple. I am saying to cache the PATH in some small env file that will assure only minimal amount of code is run. Why optimize existing code and risk breaking?

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Jun 9, 2016

Collaborator

The minimal amount of code is already being run to set everything up correctly. The only way I know of to speed things up would involve cutting corners, which would break various things for various people.

If anyone has any ideas of how to improve speed while maintaining correctness, I'd love to hear them! Arbitrary hacks don't actually help anyone, they just create future issues when they inevitably conflict with changes inside nvm itself.

Collaborator

ljharb commented Jun 9, 2016

The minimal amount of code is already being run to set everything up correctly. The only way I know of to speed things up would involve cutting corners, which would break various things for various people.

If anyone has any ideas of how to improve speed while maintaining correctness, I'd love to hear them! Arbitrary hacks don't actually help anyone, they just create future issues when they inevitably conflict with changes inside nvm itself.

@defunctzombie

This comment has been minimized.

Show comment
Hide comment
@defunctzombie

defunctzombie Aug 13, 2016

Is there a 90% use cases win here than can be made to make this performant for the common cases and opening a new shell case but still maintain functionality for the edge cases?

defunctzombie commented Aug 13, 2016

Is there a 90% use cases win here than can be made to make this performant for the common cases and opening a new shell case but still maintain functionality for the edge cases?

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Aug 13, 2016

Collaborator

Not that I can think of, but I'm open to suggestions.

There are two categories of slowness that affect "opening a new shell":

  1. if you have a default specified, this seems to slow things down noticeably. I'm not sure why, and this probably could be improved. Current workaround: add --no-use to the line in your profile that sources nvm.sh, eg [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" --no-use # This loads nvm
  2. npm config get prefix is slow upon actually using a version, which is most frustrating when loading a new shell that uses a default version. There are a litany of ways that the prefix could be specified, so doing it outside of npm ends up being not much slower than calling npm itself, in addition to being more brittle. Sadly, while the majority case is that users won't have a prefix specified, it's such a massive footgun that I remain convinced that it is better for the ecosystem as a whole for everybody to suffer this slowdown rather than to risk even a single person unknowingly using nvm with a prefix set.
Collaborator

ljharb commented Aug 13, 2016

Not that I can think of, but I'm open to suggestions.

There are two categories of slowness that affect "opening a new shell":

  1. if you have a default specified, this seems to slow things down noticeably. I'm not sure why, and this probably could be improved. Current workaround: add --no-use to the line in your profile that sources nvm.sh, eg [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" --no-use # This loads nvm
  2. npm config get prefix is slow upon actually using a version, which is most frustrating when loading a new shell that uses a default version. There are a litany of ways that the prefix could be specified, so doing it outside of npm ends up being not much slower than calling npm itself, in addition to being more brittle. Sadly, while the majority case is that users won't have a prefix specified, it's such a massive footgun that I remain convinced that it is better for the ecosystem as a whole for everybody to suffer this slowdown rather than to risk even a single person unknowingly using nvm with a prefix set.
@knpwrs

This comment has been minimized.

Show comment
Hide comment
@knpwrs

knpwrs commented Aug 13, 2016

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Aug 13, 2016

Collaborator

The edge cases of your approach are legion, and I wouldn't want to put something like that into nvm unless it was robust. Adding --no-use should be sufficient to eagerly load nvm itself, and then you can defer nvm use if you really want to.

(for example, yours doesn't cover pre-0.12 node versions nor io.js versions, and won't work in the future when i add RCs and nightly builds, and I suspect that line isn't POSIX-compliant)

Collaborator

ljharb commented Aug 13, 2016

The edge cases of your approach are legion, and I wouldn't want to put something like that into nvm unless it was robust. Adding --no-use should be sufficient to eagerly load nvm itself, and then you can defer nvm use if you really want to.

(for example, yours doesn't cover pre-0.12 node versions nor io.js versions, and won't work in the future when i add RCs and nightly builds, and I suspect that line isn't POSIX-compliant)

@knpwrs

This comment has been minimized.

Show comment
Hide comment
@knpwrs

knpwrs Aug 14, 2016

I wasn't necessarily suggesting that my approach (actually, from a reddit thread) would be appropriate for everyone. Just that lazy loading is something that lots of people are doing already and an official solution might be worth considering.

The advantage of what I'm doing as opposed to --no-use followed by nvm use is that CLI utilities that are installed by npm are themselves lazy loaded, I don't need to think about it.

knpwrs commented Aug 14, 2016

I wasn't necessarily suggesting that my approach (actually, from a reddit thread) would be appropriate for everyone. Just that lazy loading is something that lots of people are doing already and an official solution might be worth considering.

The advantage of what I'm doing as opposed to --no-use followed by nvm use is that CLI utilities that are installed by npm are themselves lazy loaded, I don't need to think about it.

@alangpierce

This comment has been minimized.

Show comment
Hide comment
@alangpierce

alangpierce Aug 24, 2016

I was able to speed up shell init significantly by running nvm.sh with --no-use and then just setting the PATH directly to what nvm use would have done, like this:

NODE_VERSION="v4.4.7"
. "$NVM_DIR/nvm.sh" --no-use
export PATH="${PATH}:${NVM_DIR}/versions/node/${NODE_VERSION}/bin"

As far as I can tell, for my simple case, this seems to work just as well as nvm use (although it would be good to hear if there's other state that's not being set correctly). Since it hard-codes my default node version, it obviously is not something that could be put in nvm, but for me it's not that much trouble to maintain that line.

alangpierce commented Aug 24, 2016

I was able to speed up shell init significantly by running nvm.sh with --no-use and then just setting the PATH directly to what nvm use would have done, like this:

NODE_VERSION="v4.4.7"
. "$NVM_DIR/nvm.sh" --no-use
export PATH="${PATH}:${NVM_DIR}/versions/node/${NODE_VERSION}/bin"

As far as I can tell, for my simple case, this seems to work just as well as nvm use (although it would be good to hear if there's other state that's not being set correctly). Since it hard-codes my default node version, it obviously is not something that could be put in nvm, but for me it's not that much trouble to maintain that line.

MoOx added a commit to MoOx/setup that referenced this issue Aug 29, 2016

ulfmagnetics added a commit to ulfmagnetics/dotfiles that referenced this issue Dec 7, 2016

@oncletom

This comment has been minimized.

Show comment
Hide comment
@oncletom

oncletom Dec 10, 2016

I totally understand the need to avoid hacky solutions and to cut corners – I guess there are plenty; shells are so versatiles.

Would it be reasonable to know what could help improve solve the problem though? What is the path to resolution?

'cause if using nvm decreases the developer experience in the terminal, it defeats the purpose of the tool itself – and nvm is great so it does not deserve to be undermined by this loading performance issue.

oncletom commented Dec 10, 2016

I totally understand the need to avoid hacky solutions and to cut corners – I guess there are plenty; shells are so versatiles.

Would it be reasonable to know what could help improve solve the problem though? What is the path to resolution?

'cause if using nvm decreases the developer experience in the terminal, it defeats the purpose of the tool itself – and nvm is great so it does not deserve to be undermined by this loading performance issue.

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Dec 11, 2016

Collaborator

@oncletom very diplomatically said. the main slowdown is npm config get prefix - if someone can come up with a reliable way to replicate that without the slowdown, I'd be happy to use that instead. See npm/npm#14458 for some background.

Collaborator

ljharb commented Dec 11, 2016

@oncletom very diplomatically said. the main slowdown is npm config get prefix - if someone can come up with a reliable way to replicate that without the slowdown, I'd be happy to use that instead. See npm/npm#14458 for some background.

AGhost-7 added a commit to AGhost-7/nvm that referenced this issue Apr 18, 2017

Remove prefix check to speed up performance
People who actually read the docs should not suffer - see creationix#860 and creationix#703
@0xcaff

This comment has been minimized.

Show comment
Hide comment
@0xcaff

0xcaff Jun 17, 2017

Here's a script to lazy load nvm when using bash as your shell:

# Set up NVM
# Lazily initialize nvm to keep shell start up time fast.
export NVM_DIR="$HOME/.nvm"
export NVM_SH="$NVM_DIR/nvm.sh"
# https://github.com/creationix/nvm/issues/860
declare -a NODE_GLOBALS=(`find $NVM_DIR/versions/node -maxdepth 3 -type l -wholename '*/bin/*' | xargs -n1 basename | sort | uniq`)

NODE_GLOBALS+=("node")
NODE_GLOBALS+=("nvm")

load_nvm () {
    # echo "Loading NVM..."
    [ -s "$NVM_SH" ] && source "$NVM_SH"
    # echo "Loaded NVM"
}

unhook_nvm_load () {
    # echo "Unhooking NVM Lazy Loader"
    for cmd in "${NODE_GLOBALS[@]}"; do
        unset -f "${cmd}"
    done
    # echo "Unhooked NVM Lazy Loader"
}

for cmd in "${NODE_GLOBALS[@]}"; do
    eval "function ${cmd} () { unhook_nvm_load; load_nvm; ${cmd} \$@; }"
done

0xcaff commented Jun 17, 2017

Here's a script to lazy load nvm when using bash as your shell:

# Set up NVM
# Lazily initialize nvm to keep shell start up time fast.
export NVM_DIR="$HOME/.nvm"
export NVM_SH="$NVM_DIR/nvm.sh"
# https://github.com/creationix/nvm/issues/860
declare -a NODE_GLOBALS=(`find $NVM_DIR/versions/node -maxdepth 3 -type l -wholename '*/bin/*' | xargs -n1 basename | sort | uniq`)

NODE_GLOBALS+=("node")
NODE_GLOBALS+=("nvm")

load_nvm () {
    # echo "Loading NVM..."
    [ -s "$NVM_SH" ] && source "$NVM_SH"
    # echo "Loaded NVM"
}

unhook_nvm_load () {
    # echo "Unhooking NVM Lazy Loader"
    for cmd in "${NODE_GLOBALS[@]}"; do
        unset -f "${cmd}"
    done
    # echo "Unhooked NVM Lazy Loader"
}

for cmd in "${NODE_GLOBALS[@]}"; do
    eval "function ${cmd} () { unhook_nvm_load; load_nvm; ${cmd} \$@; }"
done
@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Jun 17, 2017

Collaborator

@0xcaff see #860 (comment) (specifically, what if a global is available in one version of node but not another, and i try to use it)

Collaborator

ljharb commented Jun 17, 2017

@0xcaff see #860 (comment) (specifically, what if a global is available in one version of node but not another, and i try to use it)

@0xcaff

This comment has been minimized.

Show comment
Hide comment
@0xcaff

0xcaff Jun 17, 2017

Good, point. It will fail in that case. I only use nvm with one version of node, the latest, so it always works for me. I agree that is is not optimal.

0xcaff commented Jun 17, 2017

Good, point. It will fail in that case. I only use nvm with one version of node, the latest, so it always works for me. I agree that is is not optimal.

@defunctzombie

This comment has been minimized.

Show comment
Hide comment
@defunctzombie

defunctzombie Jun 20, 2017

@ljharb earlier comments suggest that there are corner cases to consider for iojs and older nodejs versions. If a user is no longer interested in those, is there a way to make this script faster?

defunctzombie commented Jun 20, 2017

@ljharb earlier comments suggest that there are corner cases to consider for iojs and older nodejs versions. If a user is no longer interested in those, is there a way to make this script faster?

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Jun 21, 2017

Collaborator

@defunctzombie the speed here is 100% unrelated to node or io.js version; as it's solely caused by every version of npm running npm config get prefix slowly. Can you point me to a specific comment that claims it's related?

Collaborator

ljharb commented Jun 21, 2017

@defunctzombie the speed here is 100% unrelated to node or io.js version; as it's solely caused by every version of npm running npm config get prefix slowly. Can you point me to a specific comment that claims it's related?

@TylerBrock

This comment has been minimized.

Show comment
Hide comment
@TylerBrock

TylerBrock Jun 24, 2017

Why does npm config get prefix run so slowly? Presumably it's doing a trivial amount of work.

TylerBrock commented Jun 24, 2017

Why does npm config get prefix run so slowly? Presumably it's doing a trivial amount of work.

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Jun 25, 2017

Collaborator

@TylerBrock it's not as trivial as you'd think; it merges any .npmrc file crawling up to /, and then it overlays, case-insensitive, any NPM_CONFIG_BLAH env vars (ie, NpM_cOnFig_blah works too) - and then overlays some other env vars, and then comes up with a config. That said, it should still be quick. What would be ideal is if npm created a separate package, that they consumed in the npm CLI, that covered all npm config commands - then we could use that :-)

Collaborator

ljharb commented Jun 25, 2017

@TylerBrock it's not as trivial as you'd think; it merges any .npmrc file crawling up to /, and then it overlays, case-insensitive, any NPM_CONFIG_BLAH env vars (ie, NpM_cOnFig_blah works too) - and then overlays some other env vars, and then comes up with a config. That said, it should still be quick. What would be ideal is if npm created a separate package, that they consumed in the npm CLI, that covered all npm config commands - then we could use that :-)

@TylerBrock

This comment has been minimized.

Show comment
Hide comment
@TylerBrock

TylerBrock Jun 25, 2017

Ok, fair enough. I might not know enough about the inner workings and capabilities here but why does nvm need to know what npm thinks the prefix is? Can we skip this interrogation altogether?

Perhaps we could set some sort of NVM_PATH that gets us the root where we store all the node installations. Then we can get the version of node we want (or default) from the closest nvmrc to the CWD thereby breaking all this down to a simple path concatenation.

Example:

nvm config get prefix on my computer says: /Users/tbrock/.nvm/versions/node/v6.11.0
If my NVM_PATH was $HOME/.nvm then I'd just read the .nvmrc closest to me and do:

export prefix=${NVM_PATH}/versions/node/${NVM_NODE_VERSION}

Searching upwards to the find the closest nvmrc should take some time but it shouldn't take 500ms+. Every other verison manager has figured out how to make this operation fast we should be able to do it as well.

TylerBrock commented Jun 25, 2017

Ok, fair enough. I might not know enough about the inner workings and capabilities here but why does nvm need to know what npm thinks the prefix is? Can we skip this interrogation altogether?

Perhaps we could set some sort of NVM_PATH that gets us the root where we store all the node installations. Then we can get the version of node we want (or default) from the closest nvmrc to the CWD thereby breaking all this down to a simple path concatenation.

Example:

nvm config get prefix on my computer says: /Users/tbrock/.nvm/versions/node/v6.11.0
If my NVM_PATH was $HOME/.nvm then I'd just read the .nvmrc closest to me and do:

export prefix=${NVM_PATH}/versions/node/${NVM_NODE_VERSION}

Searching upwards to the find the closest nvmrc should take some time but it shouldn't take 500ms+. Every other verison manager has figured out how to make this operation fast we should be able to do it as well.

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Jun 25, 2017

Collaborator

@TylerBrock because if npm's prefix is overridden (from the nvm-managed path), then nvm can't work properly - and people often have it set from trying incorrect tutorials prior to trying nvm.

I agree that it shouldn't be slow; I believe it's because npm config always bootstraps the entire npm CLI even if it's only running the config stuff.

(Also note it's npm config, not nvm config, and npmrc, not nvmrc)

I certainly could just always override the prefix setting; but since it can be set any number of ways, it's much safer to require the user to manually remove the override.

Collaborator

ljharb commented Jun 25, 2017

@TylerBrock because if npm's prefix is overridden (from the nvm-managed path), then nvm can't work properly - and people often have it set from trying incorrect tutorials prior to trying nvm.

I agree that it shouldn't be slow; I believe it's because npm config always bootstraps the entire npm CLI even if it's only running the config stuff.

(Also note it's npm config, not nvm config, and npmrc, not nvmrc)

I certainly could just always override the prefix setting; but since it can be set any number of ways, it's much safer to require the user to manually remove the override.

AGhost-7 added a commit to AGhost-7/nvm that referenced this issue Aug 18, 2017

Remove prefix check to speed up performance
People who actually read the docs should not suffer - see creationix#860 and creationix#703
@caleb531

This comment has been minimized.

Show comment
Hide comment
@caleb531

caleb531 Aug 26, 2017

@ljharb Maybe this is a terrible idea, but hear me out:

For the people who read the docs, perhaps some sort of --bypass-prefix-check option could be added, which the user would supply when sourcing nvm.sh in their shell config. The use of the option would be the user's affirmation that they are not using a prefix and that they understand the risks of having one set.

You see, I'm currently using n to manage my multiple node versions, but I'm not a fan of the way it manages node (i.e. use of sudo and large binaries in /usr/local/bin). I've been considering nvm for a while now, but the shell startup slowdown has always been an absolute deal-breaker for me.

At this point, I'm sorely tempted to throw in the towel and just use @AGhost-7's recent patch (AGhost-7@892dbb9), which removes the prefix check entirely. At the same time, however, I'd prefer to use the official nvm so I can benefit from future improvements to the software.

caleb531 commented Aug 26, 2017

@ljharb Maybe this is a terrible idea, but hear me out:

For the people who read the docs, perhaps some sort of --bypass-prefix-check option could be added, which the user would supply when sourcing nvm.sh in their shell config. The use of the option would be the user's affirmation that they are not using a prefix and that they understand the risks of having one set.

You see, I'm currently using n to manage my multiple node versions, but I'm not a fan of the way it manages node (i.e. use of sudo and large binaries in /usr/local/bin). I've been considering nvm for a while now, but the shell startup slowdown has always been an absolute deal-breaker for me.

At this point, I'm sorely tempted to throw in the towel and just use @AGhost-7's recent patch (AGhost-7@892dbb9), which removes the prefix check entirely. At the same time, however, I'd prefer to use the official nvm so I can benefit from future improvements to the software.

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Aug 26, 2017

Collaborator

@caleb531 i'd be all in favor of that, except that the instant there's an official way to bypass the checking, it's going to show up in tweets, blog posts, and SO posts where people just blindly copy-paste it to "get things working". That's not going to ensure that the majority "understand the risks".

Collaborator

ljharb commented Aug 26, 2017

@caleb531 i'd be all in favor of that, except that the instant there's an official way to bypass the checking, it's going to show up in tweets, blog posts, and SO posts where people just blindly copy-paste it to "get things working". That's not going to ensure that the majority "understand the risks".

@caleb531

This comment has been minimized.

Show comment
Hide comment
@caleb531

caleb531 Aug 26, 2017

@ljharb Fair enough. Here's another idea: have nvm only check the prefix until it encounters the default prefix (either because the user removed their custom prefix, or if the prefix was never changed in the first place).

The logic could work like the following, probably using some file under NVM_DIR to represent when nvm detects the default prefix (I'll call this the "flag file"). If this flag file doesn't exist, always check the prefix as needed. If it does exist and/or it contains the right data, skip the prefix check.

  1. On installation, check the prefix. Abort the installation if a custom prefix is set.
  2. When the user invokes nvm for the first time (post-installation), check the prefix. If the prefix is non-default, warn the user and abort. If the prefix is the default, update the flag file
  3. Whenever nvm is updated or reinstalled, reset the flag file so that the prefix will be checked again.

Theoretically, you could check the prefix periodically on a certain interval (i.e. if the last check was more than a week ago), but that feels rather arbitrary. I'd rather go with the above approach: checking on installation, update/reinstall, and first invocation of nvm—aborting in all cases if the prefix is incorrect.

I personally think this is a reasonable approach, though it really depends on how likely the user would change the prefix after they've installed nvm. I suspect that's not very likely, but I'd be curious to hear what anyone else thinks.

caleb531 commented Aug 26, 2017

@ljharb Fair enough. Here's another idea: have nvm only check the prefix until it encounters the default prefix (either because the user removed their custom prefix, or if the prefix was never changed in the first place).

The logic could work like the following, probably using some file under NVM_DIR to represent when nvm detects the default prefix (I'll call this the "flag file"). If this flag file doesn't exist, always check the prefix as needed. If it does exist and/or it contains the right data, skip the prefix check.

  1. On installation, check the prefix. Abort the installation if a custom prefix is set.
  2. When the user invokes nvm for the first time (post-installation), check the prefix. If the prefix is non-default, warn the user and abort. If the prefix is the default, update the flag file
  3. Whenever nvm is updated or reinstalled, reset the flag file so that the prefix will be checked again.

Theoretically, you could check the prefix periodically on a certain interval (i.e. if the last check was more than a week ago), but that feels rather arbitrary. I'd rather go with the above approach: checking on installation, update/reinstall, and first invocation of nvm—aborting in all cases if the prefix is incorrect.

I personally think this is a reasonable approach, though it really depends on how likely the user would change the prefix after they've installed nvm. I suspect that's not very likely, but I'd be curious to hear what anyone else thinks.

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Dec 20, 2017

Collaborator

#1679 exhaustively covers all the environment variables that npm checks for, and I'm relatively sure it's pretty fast.

I believe the remaining piece to remove npm usage entirely will be to check .npmrc files upwards to /, and error out if any of the lines has a "prefix" setting.

Collaborator

ljharb commented Dec 20, 2017

#1679 exhaustively covers all the environment variables that npm checks for, and I'm relatively sure it's pretty fast.

I believe the remaining piece to remove npm usage entirely will be to check .npmrc files upwards to /, and error out if any of the lines has a "prefix" setting.

@ouchxp

This comment has been minimized.

Show comment
Hide comment
@ouchxp

ouchxp Jan 18, 2018

Here's my performance hacks, it help nvm use speed up to 54ms
ouchxp@dd02606

ouchxp commented Jan 18, 2018

Here's my performance hacks, it help nvm use speed up to 54ms
ouchxp@dd02606

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment