33 Comments
User's avatar
Alabhya's avatar

My startup time was around 400ms, mostly because of NVM. I was using bash, and switched over to zsh. I'm now lazy loading NVM using the oh-my-zsh plugin and now my startup time is around 100ms. Not very fast but an improvement nonetheless. Thanks!!

Expand full comment
Ruslan Prakapchuk's avatar

https://github.com/Schniz/fnm solved my problems with nvm startup time, highly recommend to give it a go!

Expand full comment
Mustaque Ahmed's avatar

Thanks for writing post. This gave me curiousity to check my shell startup time. I found it takes ~1.2 seconds to load SHELL and after fixing nvm (culprit who was taking most of time) it has reduced to 0.121ms which is great 👍

Thank you once again.

Expand full comment
Cristian Dominguez's avatar

➜ dotfiles git:(main) ✗ for i in $(seq 1 10); do time $SHELL -i -c exit; done

$SHELL -i -c exit 0.03s user 0.00s system 99% cpu 0.030 total

$SHELL -i -c exit 0.02s user 0.01s system 99% cpu 0.029 total

$SHELL -i -c exit 0.02s user 0.00s system 98% cpu 0.029 total

$SHELL -i -c exit 0.02s user 0.01s system 99% cpu 0.027 total

$SHELL -i -c exit 0.02s user 0.00s system 98% cpu 0.028 total

$SHELL -i -c exit 0.02s user 0.01s system 99% cpu 0.028 total

$SHELL -i -c exit 0.03s user 0.00s system 99% cpu 0.027 total

$SHELL -i -c exit 0.02s user 0.00s system 99% cpu 0.027 total

$SHELL -i -c exit 0.02s user 0.01s system 99% cpu 0.028 total

$SHELL -i -c exit 0.02s user 0.01s system 99% cpu 0.028 total

but it used to take ~500ms with nvm, then found about lazy-loading it on GH (linked blogs show similar solutions):

https://github.com/nvm-sh/nvm/issues/1242#issuecomment-346946356

I'll give the zsh comp optimizations a try and report back, thanks!

Expand full comment
Vegard Stikbakke's avatar

Wow, thanks for this! I got it down from 400ms to 120ms.

Expand full comment
Thorsten Ball's avatar

What was the biggest culprit?

Expand full comment
Vegard Stikbakke's avatar

Multiple (!) times loading compinit, several completions for CLI tools I'm not using anymore, auto updates. I'm still using oh my zsh, that's the main culprit now. Getting a new work machine next week, will be ditching oh my zsh then and using your last post as a guide!

Expand full comment
Thorsten Ball's avatar

Ah! Multiple compinits — I've had that too. And yes, several completions can get you :)

Expand full comment
Tushar Dahiya's avatar

❯ time zsh -i -c exit

zsh -i -c exit 0.40s user 0.17s system 95% cpu 0.606 total

Expand full comment
Thorsten Ball's avatar

At least 400ms need to go :)

Expand full comment
Tushar Dahiya's avatar

yep, working on it

Expand full comment
Fábio Pinto Fortkamp's avatar

This (after many re-readings) encouraged me to change my shell from zsh to fish - just to start with a fresh state! Keep up the good work!

Expand full comment
asdf0x2199's avatar

I've also had this problem:

```

$ time zsh -i -c exit

zsh -i -c exit 0.23s user 0.26s system 38% cpu 0.873 total

```

After removing:

- oh-my-zsh

- autoenv

- starship

- using envvars instead of functions

- removing zsh feat (autocompletion, themes, ...) and all non-critical custom functions

```

$ hyperfine zsh -i -c exit

Benchmark 1: zsh

Time (mean ± σ): 32.0 ms ± 3.8 ms [User: 1.3 ms, System: 2.7 ms]

Range (min … max): 26.8 ms … 43.4 ms 29 runs

```

As a side note, none of this is a problem on Linux. macOS is terrible at dealing with this... but I have to use Mac for work.

Expand full comment
shivam's avatar

mine was 520 ms. switched to starship, now it's 70ms

Expand full comment
angrod's avatar

I don't care how long the shell is starting (currently around 400ms) as long as I have immediatly the prompt to type.

What changed my approach is Z-Init (Zi) https://github.com/zdharma/zi#calling-compinit-without-turbo-mode which lazy load a lot of things. So I can keep the "fluff" and they just appear as I have already started to type my commands.

Expand full comment
Cyril C's avatar

If you want to easily compare the timing of a command, use the multitime tool

https://tratt.net/laurie/src/multitime/

Here is what it looks like on my computer:

```

[cyril:~] % multitime -n 10 zsh -i -c exit

===> multitime results

1: zsh -i -c exit

Mean Std.Dev. Min Median Max

real 0.219 0.019 0.188 0.217 0.262

user 0.118 0.013 0.103 0.119 0.142

sys 0.062 0.011 0.047 0.064 0.082

[cyril:~] % multitime -n 10 bash -i -c exit

exit

exit

exit

exit

exit

exit

exit

exit

exit

exit

===> multitime results

1: bash -i -c exit

Mean Std.Dev. Min Median Max

real 0.209 0.008 0.203 0.206 0.232

user 0.176 0.007 0.167 0.176 0.188

sys 0.031 0.008 0.020 0.034 0.045

```

Expand full comment
tux0r's avatar

I won.

; time rc -i -c exit

0,00 real 0,00 user 0,00 sys

; time rc -i -c exit

0,00 real 0,00 user 0,00 sys

; time rc -i -c exit

0,00 real 0,00 user 0,00 sys

; time rc -i -c exit

0,00 real 0,00 user 0,00 sys

; time rc -i -c exit

0,00 real 0,00 user 0,00 sys

Expand full comment
tux0r's avatar

Side note: Even any shell other than rc should be pretty damn fast. If it's not, because too much configuration junk has accumulated (example: do you REALLY need a coloured prompt?), then it probably doesn't matter which shell you use. A shell is not an IDE, so it shouldn't be set up as if it were ;-)

Expand full comment
Thorsten Ball's avatar

Ehhh, I get where you're coming from, but: https://blog.sanctum.geek.nz/series/unix-as-ide/

It /can/ be part of an IDE :)

Expand full comment
tux0r's avatar

Sure, and I (sometimes) start Acme which is basically a tiled toolbar for shell commands, but the point I was trying to make is that the commands you run inside your shell are the relevant part. I mean, (almost?) everything you do on the shell works the same way in any other shell (give or take certain library features, like readline's tab completion, which is also an optional part of rc anyway). It makes no sense to have a "batteries-included" shell with a long startup time. The batteries are your external commands.

Expand full comment
telemachus's avatar

I really like having information about git status in my prompt (if I'm in a git repository), but as you say that can slow down "time to next prompt" to a noticeable degree (especially in large git repos). As an alternative to removing the information, however, I recommend that people look for async solutions. I'm currently using woefe/git-prompt.zsh, and it removes the drag from git information completely. (I've also seen solutions for zsh that use a daemon or a cache, but I haven't investigated those.)

https://github.com/woefe/git-prompt.zsh

Another good tool for timing how long it takes to get a next prompt is zsh-prompt-benchmark.

https://github.com/romkatv/zsh-prompt-benchmark

Expand full comment
Thorsten Ball's avatar

Thanks for those links! I still have git information in my prompt, because I need it, but I also think _just_ running git is not that bad.

Expand full comment
Tim's avatar

With this test on my computer, bash takes 7 ms to start, or occasionally 6 ms. What is everybody doing that's taking 10x or 100x as long? That's bonkers.

Expand full comment
Thorsten Ball's avatar

Different tools with shell hooks: NVM, Docker, asdf, rbenv, ...

Expand full comment
Divyendu Singh's avatar

You are 10x faster than my macOS, room to improve for me

```

$ [~] $ time zsh -i -c exit

zsh -i -c exit 0.32s user 0.48s system 99% cpu 0.794 total

$ [~] $ time zsh -i -c exit

zsh -i -c exit 0.32s user 0.47s system 98% cpu 0.803 total

$ [~] $ time zsh -i -c exit

zsh -i -c exit 0.33s user 0.51s system 98% cpu 0.849 total

$ [~] $ time zsh -i -c exit

zsh -i -c exit 0.32s user 0.52s system 99% cpu 0.843 total

$ [~] $ time zsh -i -c exit

zsh -i -c exit 0.32s user 0.50s system 100% cpu 0.813 total

```

And ~~2x slower than my primary dev environment VM

```

divyendusingh@ubuntu:~/zoid/plv8ify$ time bash -i -c exit

exit

real 0m0.043s

user 0m0.003s

sys 0m0.008s

divyendusingh@ubuntu:~/zoid/plv8ify$ time bash -i -c exit

exit

real 0m0.041s

user 0m0.004s

sys 0m0.004s

divyendusingh@ubuntu:~/zoid/plv8ify$ time bash -i -c exit

exit

real 0m0.011s

user 0m0.004s

sys 0m0.003s

divyendusingh@ubuntu:~/zoid/plv8ify$ time bash -i -c exit

exit

real 0m0.010s

user 0m0.005s

sys 0m0.002s

divyendusingh@ubuntu:~/zoid/plv8ify$ time bash -i -c exit

exit

real 0m0.011s

user 0m0.004s

sys 0m0.003s

```

Probably why it didn't bother me as much! (not because I don't notice slowness in terminal, but other parts of my dev setup are slower!)

Expand full comment
Will's avatar

Weighted clown shoes is such a good analogy!

I use hyperfine as a fancier `for i in $(seq)` and a `PS4=` trick to profile times of everything the rc file is doing: https://github.com/WillForan/dotconf/tree/master/bin/profile-bashrc

Expand full comment
Thorsten Ball's avatar

Fancy! I like it!

Expand full comment
owl's avatar

Measuring with `hyperfine`, I get 13ms, with a "fancy" prompt. I wrote the prompt in Zig, which makes it easy to ensure that there are no allocations, no syscalls other than one `write()`, etc. I never measured it like this, but seems like it worked out quite well.

Expand full comment