Debugging Shell Startup Performance

Cutting the time it takes for my shell to start in half by getting rid of a tool I have been relying on for the last couple of years.

It has been a couple of years since I switched from oh-my-zsh to zsh4humans. Since then, I never had to worry about any performance related issues when starting my shell. It always felt instant. Until now.

The Problem

A couple of months ago, I noticed considerable lag when starting a new instance of my shell. I’m not really sensitive to shell startup time, since I’m not a heavy shell user (mostly inside VSCode when coding and sometimes in iTerm to trigger one-off commands or navigate something buried in a hidden folder). But startup times (more specifically, time to first command or first_command_lag in zsh-bench lingo) measured in seconds instead of milliseconds is too much. So I digged into my ./dotfiles to find the culprit.

Finding the culprit

I commented out half of my .zshrc and checked again. And repeated that a couple of times until I was pretty sure the issue was with my python shell config, specifically with those two lines:

eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

They alone were responsible for ~300ms or half of the total first command lag time. And I was not alone.

Is there a workaround?

Some people seem to be happy when shell detection and rehashing is skipped

eval "$(pyenv init - --no-rehash zsh)"
eval "$(pyenv virtualenv-init -)"

but for me, the improvement (around ~50ms) was marginal at best.

When I removed all the environments I created over the last year (n=68), I gained ~150ms, but pyenv was still using ~170ms to load (and now it wasn’t even doing anything anymore for me).

Time to switch

So it is probably time to find another tool that provides similar utility to pyenv with less impact on my shell performance. And whenever I hear performance, I think to myself: “maybe somebody has rewritten this in Rust”.

And, of course, Astral has. It is called uv. And in the next article, I will describe how that switch went.