Autoloaded functions and startup speed

Zsh · Expert · 12 min

A shell that opens instantly is the result of knowing where your startup time goes.

Autoloading keeps .zshrc small. Put each function in its own file named after it, in a directory on fpath, and declare it with autoload -Uz name. The file holds only the function body. Nothing is read until the first call. -U disables alias expansion while loading (so your aliases cannot break it) and -z selects zsh-style loading. Most of zsh's bundled features (compinit, vcs_info, zmv) are autoloaded functions.

typeset -U path keeps the path array, which is tied to PATH, free of duplicates; edit path=(dir $path) like any array. The same works for fpath.

To find slow startup, put zmodload zsh/zprof at the very top of .zshrc and zprof at the bottom; it prints which functions took the most time. time zsh -i -c exit measures the total. Usual suspects:

  • compinit checking and rebuilding the dump every time: call compinit -C when the dump is recent,
  • version-manager init scripts (nvm, pyenv, conda): lazy-load them with a stub function that replaces itself on first use,
  • frameworks loading many plugins, and commands like brew --prefix run on each start.

zcompile compiles scripts and function directories into .zwc wordcode files that load faster; zsh uses them automatically when they are newer than the source.

Example

# ~/.zshrc, first line:
zmodload zsh/zprof                    # start profiling
typeset -U path fpath                 # no duplicate entries
path=(~/.local/bin $path)             # tied to $PATH
fpath=(~/.zsh/functions $fpath)
autoload -Uz mkcd                     # body lives in ~/.zsh/functions/mkcd:
#   mkdir -p -- "$1" && cd -- "$1"
nvm() { unfunction nvm; source ~/.nvm/nvm.sh; nvm "$@"; }   # lazy-load
autoload -Uz compinit
setopt EXTENDED_GLOB
if [[ -n ~/.zcompdump(#qN.mh+24) ]]; then
  compinit                            # dump older than 24 h: full check
else
  compinit -C                         # recent dump: skip the check
fi
zcompile ~/.zshrc                     # writes ~/.zshrc.zwc
# last line:
zprof | head -n 15                    # functions sorted by time spent

Remember

  • autoload -Uz name loads ~/fpath/name on first call
  • zmodload zsh/zprof + zprof finds slow startup code
  • Lazy-load heavy tools and use compinit -C with a fresh dump

Watch out for

  • Running eval "$(slow-tool init)" for several tools on every startup instead of lazy-loading.

Check yourself

  1. What does `autoload -Uz mkcd` do immediately?
  2. How do you see which parts of .zshrc are slow?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: zsh.sourceforge.io/Doc/Release/Functions.html#Autoloading-Functions