Summary
We compare three modes of loading ZSH themes and plugins: with plain source
, with zplugin in non-turbo mode, and with zplugin in turbo mode.
No performance difference is observed between the tested loading modes. With zplugin in turbo mode the perceived loading latency is higher because typed characters don’t appear on the screen until loading finishes.
Methodology
The measurement methodology is recorded in the screencast. It starts with a docker command [1] that creates slow-theme.zsh
and slow-plugin.zsh
that serve as stand-ins for a slow-to-load ZSH plugin and theme respectively. Each takes one second to load.
The same docker command creates ~/.zshrc
that loads slow-theme.zsh
and slow-plugin.zsh
. It supports three modes of loading, controlled by MODE
parameter.
-
MODE=source
: Load themes and plugins by sourcing them. -
MODE=zplugin-non-turbo
: Load themes and plugins with zplugin in non-turbo mode. -
zplugin-turbo
: Load themes and plugins with zplugin in turbo mode.
While plugins and themes are being loaded, a simple prompt is displayed. Eventually it gets replaced by the “real” prompt set by slow-theme.zsh
.
Each mode of loading is tested in turn by launching ZSH and immediately starting to type echo hello world
. Observations:
-
MODE=source
andMODE=zplugin-non-turbo
:- the “loading” prompt appears instantly
- the typed characters appears on the screen
- two seconds later the real prompt appears; the part of the command that’s already been typed is there
-
MODE=zplugin-turbo
:- the same as above except that typed characters do not appear on the screen while plugins and themes are being loaded
Afterwards ZSH startup time is measured for different loading modes. This is the time between the moment zsh
is started and plugins and themes finish loading. Evidently, ZSH startup time is slightly over 2 seconds and virtually identical for all loading modes. Note that all loading modes allow for commands to be typed before loading finishes. However, with zplugin in turbo mode keyboard input is hidden until the end of the loading process, which may make the perceived loading latency higher.
Discussion
The results of this study contradict the prevailing opinion that zplugin in turbo mode speeds up loading of plugins and themes. This requires explanation.
The recently published Performance test of some of Zsh plugin managers (PTOSOZPM from now on) measures ZSH startup time when loading 28 plugins and themes. Its results indicate that zplugin in turbo mode can reduce ZSH startup time by 85% compared to zplugin in non-turbo mode.
Looking at the source code, we can see that ZSH startup time is measured as the time it takes for the following command to execute:
zsh -i -c exit
This command measures the time to source global and user ZSH configuration files. However, when starting an interactive zsh
, there is additional computation that has to happen before shell can be considered “started”. Notably, precmd
hooks and select zle widgets must run.
In our setup, zsh -i -c exit
completes in 22 ms when using zplugin in turbo mode. Recall that typed characters don’t appear on the screen for 2 seconds after starting zsh
. Thus, PTOSOZPM and our study are using two different definitions of ZSH startup latency that yield 2 orders of magnitude different results.
Let’s step back and consider which events users can see happening once they type zsh
.
- A “loading” prompt appears.
- Typed characters appear on the screen.
- The final prompt appears.
PTOSOZPM measures different latencies for different loading modes. For zplugin in turbo mode it measures how long it takes for the “loading” prompt to appear, while for all other modes it measures the time to the final prompt. This mistake is easy to make because only zplugin in turbo mode has non-empty prompt in PTOSOZPM. If a non-empty “loading” prompt is added to all loading modes (the same way as in our methodology), this measurement error becomes apparent.
Once this mistake in PTOSOZPM is corrected, we can measure 3 latencies for different loading modes.
- Time to “loading” prompt is 1 ms for all modes (if implemented the same way as in our methodology).
- Time to see the first typed character on the screen is 1 ms for all modes with one exception. For zplugin in turbo mode it’s over 100 ms.
- Time to see the final prompt is over 100 ms for all modes.
When PTOSOZPM is corrected to measure the same thing for all loading modes, its results confirm ours.
We also need to explain the widespread user reports that zplugin turbo mode results in significantly lower perceived ZSH startup latency. Our conjecture is that the appearance of the non-empty “loading” prompt gives this impression. To test it, one can load plugins and themes without turbo mode and display “loading” prompt with the code from our methodology. If our conjecture is correct, the perceived ZSH startup latency will be not higher than for zplugin in turbo mode.
All our tests with turbo mode involved zplugin ice
with wait"0"
. With these parameters plugins and themes are loaded immediately after the first precmd
hooks are run. We’ve concluded that there are no advantages to this kind of turbo mode: it doesn’t reduce ZSH startup latency and even makes the perceived latency higher. Perhaps there is value to wait"1"
, which loads plugins and themes 1 second after starting zsh
? Unfortunately, we cannot recommend this mode either. Keyboard input will still be hidden while plugins and themes are loading. So, if a user starts typing a command immediately after launching zsh
, the first few characters will appear on the screen, and then shell will appear to freeze. Once everything is loaded, prompt will refresh, all buffered characters will appear at once, and shell will unfreeze. Without turbo mode there is no shell freezing and everything will finish loading 1 second sooner, which is clearly better.
Conclusions
We conclude that zplugin turbo mode doesn’t offer any advantages (performance or otherwise) while having disadvantages.
Our confidence in this conclusion is paultry 10%. We are almost certain we’ve fucked it up somewhere.
[1] The docker command:
docker run -e LANG=en_US.utf8 -e TERM -it --rm archlinux/base bash -uec '
pacman -Sy --noconfirm zsh git curl
curl -fsSL https://raw.githubusercontent.com/zdharma/zplugin/master/doc/install.sh |
sh >/dev/null
>~/slow-theme.zsh cat <<END
repeat 10 sleep 0.1s # simulate 1 second of work
PROMPT="%1F%n%f in %4F%~%f %2F❯%f " # set prompt
END
>~/slow-plugin.zsh cat <<END
repeat 10 sleep 0.1s # simulate 1 second of work
END
>>~/.zshrc cat <<END
PROMPT="%3Floading>%f " # prompt to display while loading plugins and themes
print -Pn \$PROMPT
case \$MODE in
source)
# Load themes and plugins by sourcing them.
source ~/slow-theme.zsh
source ~/slow-plugin.zsh
;;
zplugin-non-turbo)
# Load themes and plugins with zplugin in non-turbo mode.
zplugin snippet ~/slow-theme.zsh
zplugin snippet ~/slow-plugin.zsh
;;
zplugin-turbo)
# Load themes and plugins with zplugin in turbo mode.
zplugin ice silent nocd wait"!"
zplugin snippet ~/slow-theme.zsh
zplugin ice silent nocd wait
zplugin snippet ~/slow-plugin.zsh
;;
*)
PROMPT="%2F❯%f "
;;
esac
if [[ \$EXIT == yes ]]; then
# Exit zsh as soon as all themes and plugins are loaded.
zplugin ice lucid nocd wait atload"exit 0"
zplugin snippet /dev/null
fi
TIMEFMT="
Elapsed: %mE"
# Once plugins and themes are loaded, clear the "loading" prompt.
_clear-prompt() {
print -Pn "\r%E\r"
unfunction _clear-prompt
setopt prompt_cr prompt_sp
precmd_functions=(\${(@)precmd_functions:#_clear-prompt})
}
unsetopt prompt_cr prompt_sp
precmd_functions=(\$precmd_functions _clear-prompt)
END
MODE=zplugin-turbo EXIT=yes zsh &>/dev/null
exec zsh'
More by romka
Share this recording
Link
Append ?t=30
to start the playback at 30s, ?t=3:20
to start the playback at 3m 20s.
Embed image link
Use snippets below to display a screenshot linking to this recording.
Useful in places where scripts are not allowed (e.g. in a project's README file).
HTML:
Markdown:
Embed the player
If you're embedding on your own page or on a site which permits script tags, you can use the full player widget:
Paste the above script tag where you want the player to be displayed on your page.
See embedding docs for additional options.
Download this recording
You can download this recording in asciicast v2 format, as a .cast file.
DownloadReplay in terminal
You can replay the downloaded recording in your terminal using the
asciinema play
command:
asciinema play 274210.cast
If you don't have asciinema CLI installed then see installation instructions.
Use with stand-alone player on your website
Download asciinema player from
the releases page
(you only need .js
and .css
file), then use it like this:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="asciinema-player.css" />
</head>
<body>
<div id="player"></div>
<script src="asciinema-player.min.js"></script>
<script>
AsciinemaPlayer.create(
'/assets/274210.cast',
document.getElementById('player'),
{ cols: 80, rows: 25 }
);
</script>
</body>
</html>
See asciinema player quick-start guide for full usage instructions.
Generate GIF from this recording
While this site doesn't provide GIF conversion at the moment, you can still do it yourself with the help of asciinema GIF generator utility - agg.
Once you have it installed, generate a GIF with the following command:
agg https://asciinema.org/a/274210 demo.gif
Or, if you already downloaded the recording file:
agg demo.cast demo.gif
Check agg --help
for all available options. You can change font
family and size, select color theme, adjust speed and more.
See agg manual for full usage instructions.