30 Comments
Jan 15Liked by Thorsten Ball

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

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

Expand full comment
Jan 14Liked by Thorsten Ball

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
Jan 14Liked by Thorsten Ball

➜ 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
Jan 14Liked by Thorsten Ball

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

Expand full comment
author

What was the biggest culprit?

Expand full comment

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
author

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

Expand full comment
Jan 14·edited Jan 14Liked by Thorsten Ball

❯ time zsh -i -c exit

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

Expand full comment
author

At least 400ms need to go :)

Expand full comment

yep, working on it

Expand full comment

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

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

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

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
author

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

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
Jan 17·edited Jan 17

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
author

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

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
author

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

Expand full comment

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

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
author

Fancy! I like it!

Expand full comment

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

>Think about it this way: (1) which program do you execute more often than your shell? (2) How many shells do you spawn every day? (3) How many other programs do you run every day that spawn your shell?

You seem to be conflating interactive (1, 2) and non-interactive shell invocations (3), here. The reality is that interactive shell startup time (here 0.02s for zsh with all the usual completions) doesn't matter as long as it's not noticeable; for the non-interactive case (the one spawned by other programs at a rate great enough to make that latency important), zsh is basically as instantaneous as dash or busybox (which are barren due to focus on POSIX compliance).

I guess the morale simply is "don't use zsh frameworks for wannabe ricers".

Related, a small latency comparison I did some ages ago: https://git.sr.ht/~q3cpma/interp_overhead/tree/master/item/results.txt

Expand full comment
author

Yup yup, good point! I didn't accidentally conflate this but rather brushed over it in the name of "make sure nothing runs that you don't want to run", because sometimes things end up in .zprofile, .zshenv, .zshrc etc.

Expand full comment

zsh -i -c exit 0.26s user 0.21s system 20% cpu 2.323 total

zsh -i -c exit 0.12s user 0.07s system 90% cpu 0.207 total

zsh -i -c exit 0.12s user 0.07s system 90% cpu 0.210 total

zsh -i -c exit 0.12s user 0.07s system 91% cpu 0.209 total

zsh -i -c exit 0.12s user 0.07s system 90% cpu 0.205 total

Expand full comment
author

Any idea what's taking the longest time?

Expand full comment
Jan 14·edited Jan 14

Yeah, it was pkgx.sh... switching to asdf because it is *way* faster.

I am now down to:

$SHELL -i -c exit 0.02s user 0.02s system 85% cpu 0.053 total

Expand full comment