Inspired by articles by Chai Phonbopit on ทำไม ZSH ของเราช้าจัง? + ทำให้เร็วขึ้นได้มั้ย and by Matthew J. Clemente on Speeding Up My Shell (Oh My Zsh), I can make my zsh terminal loads faster by lazy-loading nvm.
(Normally I’m not bothered by my zsh’s startup time because I don’t open new terminal windows/tabs a whole lot in a day. But if I can make things a bit faster, then why not get some marginal gains?.)
1. Measure the current startup time
zsh’s startup can be measured by running a script:
$ for i in $(seq 1 10); do /usr/bin/time zsh -i -c exit; done
# or using $SHELL - should work with bash
$ for i in $(seq 1 10); do /usr/bin/time $SHELL -i -c exit; done
(source)
seq
prints sequences of numbers. We use it withfor .. in
loop to run the commands 10 timestime
is a utility command to execute a command and then print out time used-i
runningzsh
in interactive mode - meaning we can run (or pass) commands to it to execute-c
tellszsh
to take the next part which isexit
as a command to execute, not as a parameter
My zsh startup time before optimize was ~1.1-1.2 seconds.
2. Analyze my .zshrc
file
I’m using zsh with oh-my-zsh. Whatever goes into .zshrc
file can add more startup time to zsh.
I don’t have much in my .zshrc
file. It looks like this:
# (1)
. /Users/armno/code/z/z.sh
# (2)
BASE16_SHELL="$HOME/.config/base16-shell/"
[ -n "$PS1" ] && \
[ -s "$BASE16_SHELL/profile_helper.sh" ] && \
eval "$("$BASE16_SHELL/profile_helper.sh")"
# (3)
export ZSH="/Users/armno/.oh-my-zsh"
ZSH_THEME="cloud-armno"
DISABLE_UPDATE_PROMPT="true"
plugins=(zsh-completions zsh-autosuggestions zsh-syntax-highlighting)
source $ZSH/oh-my-zsh.sh
source ~/.aliases
source ~/.functions
export LC_ALL=en_US.UTF-8
export GPG_TTY=$(tty)
export PATH="/usr/local/opt/ruby/bin:$PATH"
export PATH="/usr/local/sbin:$PATH"
# (4)
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
- Loads
z
- Loads
base16_shell
- Initialize oh-my-zsh and its plugins
- Loads
nvm
The biggest bottleneck seems to be nvm’s scripts, as mentioned by many people.
3. Disable nvm’s scripts entirely
I comment out the last 2 lines from my .zshrc
# export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
# [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
open a new termnial tab, and re-run the profiling script. With this alone, zsh’s startup time goes down from ~1.2s to ~0.17s.
This is now a lot faster, but it also means I will not be able to use nvm.
I still need nvm, sometimes.
If you’re not using nvm, I would suggest comment out some scripts/plugins from your
.zshrc
or.bashrc
file, re-run profiling script, and repeat. It should help to see which scripts are slowing down the startup time.
4. Lazy-load nvm
Lazy-loading nvm makes nvm available only when it is needed: when running nvm
, node
, npm
or gloablly installed command for the first time, and not right away then the shell starts.
It can be done by using zsh-nvm
plugin.
First, I install the plugin.
$ git clone https://github.com/lukechilds/zsh-nvm $ZSH/custom/plugins/zsh-nvm
Then in .zshrc
file, set NVM_LAZY_LOAD
environment variable to true
and add zsh-nvm
to plugins=()
list.
export NVM_LAZY_LOAD=true
plugins=(... zsh-nvm)
(note: setting NVM_LAZY_LOAD
variable must be placed before the plugins=
line in .zshrc
file.)
Then I source
the .zshrc
file so my changes take effect.
5. Measure again
Running the same script from 1. again, zsh’s startup time is now at ~0.2 seconds.
Summary
- I use the one-liner script to measure zsh’s startup time
- Instead of using nvm’s official install script, I use zsh-nvm plugin to load nvm
- I set
NVM_LAZY_LOAD
environment in my.zshrc
file totrue
- My zsh’s startup time reduces from ~1.2 seconds to ~0.2 seconds.