Optimize ZSH

Dhruva Sagar
Apr 14 · 5 min read

It’s convenient to use readily available configuration frameworks, they provide a huge bang for your buck by saving you a lot of time and effort while making your shell experience pretty awesome.

However, this convenience comes at a cost and the more plugins you enable, the more tools you use, they all add up and make ZSH sluggish, especially during start up.

Today I will introduce you to a few tricks I use to measure and profile ZSH. I will also benchmark different setups for their start up times for a comparison.

Photo by Aron Visuals on Unsplash

NOTE: All scripts I use today are available within my dotfiles/zsh repository.

Measuring start up times

I have this simple function within my zsh configs which I use from time to time to measure how fast zsh loads, it looks like this :

Simple way to benchmark your zsh start up

This is what it looks like when you execute the above function :

dhruvasagar in ~/dotfiles/zsh on master ✗
$ timezsh
0.05s user 0.04s system 99% cpu 0.088 total
0.05s user 0.03s system 99% cpu 0.087 total
0.05s user 0.03s system 100% cpu 0.087 total
0.05s user 0.03s system 100% cpu 0.087 total
0.05s user 0.03s system 100% cpu 0.087 total
0.05s user 0.04s system 100% cpu 0.090 total
0.05s user 0.03s system 100% cpu 0.088 total
0.05s user 0.04s system 100% cpu 0.089 total
0.05s user 0.03s system 99% cpu 0.088 total
0.06s user 0.04s system 100% cpu 0.094 total

My ZSH setup now takes about 0.089s on average to load, this has come down from close to ~1s with OhMyZSH and ~0.8s with Prezto (using a fair number of plugins & tools).

Profiling ZSH

ZSH ships with support for profiling and it’s quite useful. You can start the profiler with zmodload zsh/zprof and end the profiling with zprof . Anything that’s executed between those two is profiled and you can use this information to pin point the really slow scripts / configurations you may be using.

Since I do this frequently, I use this simple trick to profile ZSH on the fly :

Simple way to profile your zshrc

With the above, I can simply execute profzsh on the shell whenever I want. This is what the output looks like for my current setup :

Profiling output for my ZSH setup

As you can see it’s really useful and you can easily figure out the slow parts of your setup .

Benchmarks

I use tmux extensively, so if ZSH is slow to start up, it’s very noticeable when creating new panes / windows. It annoys me to have to wait for the prompt to appear for me to use it. In the past I moved from OhMyZSH to Prezto and recently from Prezto to vanilla ZSH in an attempt to minimize this annoying lag. The tricks I showed above were instrumental in helping me do this.

For benchmarking, I used docker to create alpine based containers where I installed and setup ZSH as :

  • Vanilla ZSH — no additional scripts.
  • My personal setup — copy over my scripts.
  • Prezto — setup prezto and add git, ruby, node, history-substring-search plugins in zpreztorc.
  • OhMyZSH — setup ohmyzsh and add git, ruby, node, history-substring-search plugins to zshrc.

NOTE: Since I used alpine as the base image, the benchmark results aren’t representative of actual setups on our day to day machines (or even my machine per say). The idea is to keep their setups very basic to compare each and see how they impact ZSH start up times.

So, here are the benchmark results :

dhruvasagar in ~/dotfiles/zsh on master ✗
$ ./benchmark
Benchmarking vanila zsh
sha256:ecdff681d558d7a9034237dd83946fb3de747a8d6e32b862799ef990c1a6016c
/bin/zsh -i -c exit 0.00s user 0.00s system 0% cpu 0.003 total
/bin/zsh -i -c exit 0.00s user 0.00s system 0% cpu 0.003 total
/bin/zsh -i -c exit 0.00s user 0.00s system 0% cpu 0.003 total
/bin/zsh -i -c exit 0.00s user 0.00s system 0% cpu 0.003 total
/bin/zsh -i -c exit 0.00s user 0.00s system 0% cpu 0.003 total
/bin/zsh -i -c exit 0.00s user 0.00s system 0% cpu 0.003 total
/bin/zsh -i -c exit 0.00s user 0.00s system 0% cpu 0.003 total
/bin/zsh -i -c exit 0.00s user 0.00s system 0% cpu 0.008 total
/bin/zsh -i -c exit 0.00s user 0.00s system 0% cpu 0.004 total
/bin/zsh -i -c exit 0.00s user 0.00s system 0% cpu 0.005 total
Benchmarking my zsh
sha256:0d0deef19895642184d592dd53d7b65a94355a8b7cd4b2e31ec6ecbe7869b1cf
/bin/zsh -i -c exit 0.14s user 0.07s system 89% cpu 0.233 total
/bin/zsh -i -c exit 0.01s user 0.00s system 50% cpu 0.020 total
/bin/zsh -i -c exit 0.01s user 0.00s system 47% cpu 0.021 total
/bin/zsh -i -c exit 0.01s user 0.00s system 48% cpu 0.021 total
/bin/zsh -i -c exit 0.00s user 0.01s system 47% cpu 0.021 total
/bin/zsh -i -c exit 0.00s user 0.02s system 87% cpu 0.023 total
/bin/zsh -i -c exit 0.00s user 0.01s system 49% cpu 0.020 total
/bin/zsh -i -c exit 0.01s user 0.00s system 46% cpu 0.022 total
/bin/zsh -i -c exit 0.00s user 0.01s system 47% cpu 0.021 total
/bin/zsh -i -c exit 0.01s user 0.00s system 45% cpu 0.022 total
Benchmarking prezto
sha256:99855b6a0ad4e8894eb0aca75917283521a7d62bb72d0cf510cdaf04e52528dc
/bin/zsh -i -c exit 0.21s user 0.09s system 95% cpu 0.315 total
/bin/zsh -i -c exit 0.03s user 0.01s system 82% cpu 0.048 total
/bin/zsh -i -c exit 0.04s user 0.00s system 80% cpu 0.049 total
/bin/zsh -i -c exit 0.04s user 0.00s system 80% cpu 0.049 total
/bin/zsh -i -c exit 0.02s user 0.02s system 81% cpu 0.049 total
/bin/zsh -i -c exit 0.03s user 0.01s system 79% cpu 0.050 total
/bin/zsh -i -c exit 0.03s user 0.01s system 78% cpu 0.051 total
/bin/zsh -i -c exit 0.04s user 0.00s system 80% cpu 0.050 total
/bin/zsh -i -c exit 0.03s user 0.01s system 81% cpu 0.049 total
/bin/zsh -i -c exit 0.03s user 0.01s system 82% cpu 0.048 total
Benchmarking ohmyzsh
sha256:6881616f7313f62f11a72c9581fa00c42f5052997d24ceea252bb57febe36e0b
/bin/zsh -i -c exit 0.17s user 0.06s system 81% cpu 0.281 total
/bin/zsh -i -c exit 0.03s user 0.00s system 48% cpu 0.062 total
/bin/zsh -i -c exit 0.02s user 0.01s system 47% cpu 0.063 total
/bin/zsh -i -c exit 0.03s user 0.00s system 47% cpu 0.063 total
/bin/zsh -i -c exit 0.02s user 0.01s system 47% cpu 0.063 total
/bin/zsh -i -c exit 0.03s user 0.00s system 47% cpu 0.063 total
/bin/zsh -i -c exit 0.03s user 0.00s system 49% cpu 0.061 total
/bin/zsh -i -c exit 0.02s user 0.01s system 48% cpu 0.062 total
/bin/zsh -i -c exit 0.02s user 0.01s system 48% cpu 0.062 total
/bin/zsh -i -c exit 0.02s user 0.01s system 47% cpu 0.064 total

Conclusion: Vanilla ZSH is really fast, my setup is ~5x slower, Prezto is ~12x slower and OhMyZSH is ~15x slower out of the box. With adding more plugins, scripts and tooling to the mix the start up times go up significantly.

NOTE: You can find my benchmark script and all the dependent scripts and files in my dotfiles/zsh repository.

Tarka Labs Blog

Tarka Labs is a team of passionate hackers, designers and product managers. We believe in experimental methods to identify the best solutions to problems instead of working on technology du jour. Visit us at https://tarkalabs.com/

Dhruva Sagar

Written by

hacker. loves to code, lives inside terminals, vim advocate, open source enthusiast.

Tarka Labs Blog

Tarka Labs is a team of passionate hackers, designers and product managers. We believe in experimental methods to identify the best solutions to problems instead of working on technology du jour. Visit us at https://tarkalabs.com/