For a while now, I have been annoyed by how sluggish my ZSH shell feels when opening. Today I finally sat down at my computer and tried to speed things up a little. But what exactly should I speed up?
After a little research, I found out that ZSH has a built-in profiler. You can enable it by adding the following as the first line of ~/.zshrc:
zmodload zsh/zprof
After that, all commands and their execution times are recorded in the background when a new shell starts. You can get a report by running the zprof command. It is best to start a new shell and exit it again immediately. You can also measure the total execution time using the handy time command.
time zsh -i -c "zprof && exit"
The output from my shell now looked like this.
num calls time self name ----------------------------------------------------------------------------------- 1) 1 120,03 120,03 97,98% 52,17 52,17 42,59% zgen-init 2) 2 27,64 13,82 22,56% 27,64 13,82 22,56% compinit 3) 1 14,58 14,58 11,90% 14,58 14,58 11,90% handle_completion_insecurities 4) 1 7,56 7,56 6,17% 7,56 7,56 6,17% _zsh_highlight_bind_widgets 5) 1 6,58 6,58 5,37% 6,45 6,45 5,26% _zsh_highlight_load_highlighters 6) 3 2,97 0,99 2,43% 2,97 0,99 2,43% colors 7) 4 1,75 0,44 1,43% 1,75 0,44 1,43% _update_prompt 8) 1 1,77 1,77 1,44% 1,74 1,74 1,42% powerlevel9k_vcs_init 9) 1 1,25 1,25 1,02% 1,25 1,25 1,02% detect-clipboard 10) 36 1,44 0,04 1,18% 1,19 0,03 0,97% set_default 11) 1 1,10 1,10 0,89% 1,10 1,10 0,89% termColors 12) 10 1,02 0,10 0,83% 1,02 0,10 0,83% compdef 13) 7 1,00 0,14 0,82% 1,00 0,14 0,82% add-zsh-hook 14) 1 5,10 5,10 4,16% 0,77 0,77 0,63% prompt_powerlevel9k_setup 15) 3 0,62 0,21 0,51% 0,62 0,21 0,51% is-at-least 16) 41 0,31 0,01 0,25% 0,31 0,01 0,25% defined 17) 1 0,18 0,18 0,15% 0,18 0,18 0,15% (anon) 18) 2 0,06 0,03 0,05% 0,06 0,03 0,05% segment_in_use 19) 1 0,08 0,08 0,07% 0,04 0,04 0,04% print_deprecation_warning 20) 2 0,04 0,02 0,03% 0,04 0,02 0,03% env_default 21) 1 0,04 0,04 0,04% 0,03 0,03 0,03% zgen 22) 1 0,02 0,02 0,02% 0,02 0,02 0,02% bashcompinit 23) 1 0,01 0,01 0,01% 0,01 0,01 0,01% zgen-saved
As you can see, a large part of the time is spent on zgen init.
Do not forget to disable the profiler in the .zshrc file again after the benchmark!
Switching from zgen to zplug
During the performance analysis, I switched my ZSH plugins from zgen to zplug, which is supposed to be considerably faster. It seems to have made quite a difference for me as well. Startup speed improved by around 20%. There is a short migration guide here: https://github.com/zplug/zplug/wiki/Migration
My logic for loading zplug plugins now looks like this:
# Check if zplug is installed if [[ ! -d ~/.zplug ]]; then git clone https://github.com/zplug/zplug ~/.zplug source ~/.zplug/init.zsh && zplug update --self fi source "${HOME}/.zplug/init.zsh" # Make sure to use double quotes to prevent shell expansion zplug "plugins/command-not-found", from:oh-my-zsh zplug "plugins/git", from:oh-my-zsh zplug "plugins/sudo", from:oh-my-zsh zplug "plugins/ssh", from:oh-my-zsh zplug "plugins/z", from:oh-my-zsh zplug "plugins/shrink-path", from:oh-my-zsh zplug "bhilburn/powerlevel9k", use:powerlevel9k.zsh-theme zplug "zsh-users/zsh-completions" zplug "zsh-users/zsh-autosuggestions" zplug "zsh-users/zsh-syntax-highlighting", defer:2 zplug "zsh-users/zsh-history-substring-search", defer:3 zplug "junegunn/fzf", use:"shell *.zsh" zplug "junegunn/fzf", as:command, hook-build:"./install --bin", use:"bin/{fzf-tmux,fzf}" # Install packages that have not been installed yet if ! zplug check; then printf "Install? \[y/N\]: " if read -q; then echo; zplug install else echo fi fi zplug load
During the analysis, I also removed a few things I did not really need. Now my shell starts lightning fast again.
time zsh -i -c "exit" zsh -i -c "exit" 0,19s user 0,09s system 113% cpu 0,244 total