Skip to content
Go back

Optimizing zsh Cold-Start Performance (Oh My Zsh)

Published:  at  16:00

One time when I opened Terminal, commands I typed didn’t appear immediately. I had to wait nearly 10 seconds before I could interact with the terminal. Time to optimize the shell cold start!

📍Benchmarking

Use the following command:

 for i in $(seq 1 10); do /usr/bin/time $SHELL -i -c exit; done
   11.95 real         1.65 user         2.83 sys
   11.92 real         1.78 user         2.93 sys
   10.72 real         1.59 user         2.48 sys
   14.81 real         1.73 user         2.83 sys
    9.60 real         1.62 user         2.56 sys
    9.47 real         1.60 user         2.60 sys
   14.17 real         1.77 user         3.00 sys
   10.08 real         1.72 user         2.96 sys
   14.51 real         1.72 user         2.85 sys
   11.64 real         1.83 user         2.91 sys

Use zsh/zprof to see which part takes the longest:

# ~/.zshrc
# 加到第一行
zmodload zsh/zprof

Run zprof and find that the slowest part is nvm:

🚀Speed

Replacement

Replace nvm with fnm. After adding the --use-on-cd parameter, it will use the Node.js version specified by the project (.node-version / .nvmrc).

// ~/.zshrc
plugins=(nvm)

# export NVM
export PATH=$HOME/.fnm:$PATH
eval "$(fnm env --use-on-cd)"

The improvement is obvious when testing again:

 for i in $(seq 1 10); do /usr/bin/time $SHELL -i -c exit; done
	1.86 real         0.44 user         0.55 sys
  2.15 real         0.46 user         0.59 sys
  1.95 real         0.43 user         0.52 sys
  2.49 real         0.43 user         0.56 sys
  4.56 real         0.45 user         0.62 sys
  3.45 real         0.48 user         0.68 sys
  1.98 real         0.43 user         0.58 sys
  1.97 real         0.45 user         0.58 sys
  1.84 real         0.43 user         0.54 sys
  1.99 real         0.44 user         0.59 sys

Caching

Use evalcache to cache eval execution.

plugins=(... evalcache)
# 一定要放在 oh-my-zsh.sh 之前
export PATH="$HOME/.jenv/bin:$PATH"
export PATH="$HOME/lolimay/.fnm:$PATH"

source $ZSH/oh-my-zsh.sh

# eval "$(jenv init -)"
_evalcache jenv init -

# eval "$(fnm env --use-on-cd)"
_evalcachefnm env --use-on-cd

Test again:

 for i in $(seq 1 10); do /usr/bin/time $SHELL -i -c exit; done                                                                                                 ─╯
  1.45 real         0.37 user         0.41 sys
  1.43 real         0.37 user         0.41 sys
  1.61 real         0.39 user         0.48 sys
  1.76 real         0.41 user         0.51 sys
  1.64 real         0.40 user         0.49 sys
  1.57 real         0.38 user         0.47 sys
  2.12 real         0.39 user         0.45 sys
  1.47 real         0.38 user         0.44 sys
  2.19 real         0.40 user         0.51 sys
  2.18 real         0.42 user         0.52 sys

Overall, startup time improved from around 11s to 1.45s. There’s still room for improvement—leaving that for a future optimization…


Share this post on:

Previous Post
📝 New MacBook Pro Setup Notes
Next Post
Module Package Design Practices in a Monorepo