Oh-My-Zsh to Prezto - 4 seconds faster

I have heard a lot about prezto. People who work with me use it and we got into this recent discussion about how slow a curl request inside your zshrc makes your shell. Strangely enough, I hadn’t noticed that it made the shell slow at all. In fact, I was saying that it didn’t make it any slower than without it. I removed it from my zshrc and checked it and indeed, it didn’t make my shell any slower.

A few days went by; I was thinking about this when I was next thinking about why I hadn’t noticed this clear increase in the time. So, I looked at the time my shell took to start up again. It was … interesting: 6 seconds.

YUP. 6 seconds on a machine with an SSD, 16 GB of RAM and an i7 processor. So, what is the problem here?

I have a zshrc that sources several other files when it loads. Going through my main zshrc and all those zsh files, I saw that everything in there was a definition: export, alias, function. Apart from this, I was sourcing a few other things: fzf and oh-my-zsh. Testing these separately, I could see that these were extremely fast and not the culprit.

That’s when I realized that I would have to profile my zshrc

echo "START: `date +%s`"

st=`date +%s`
# op
endt=`date +%s`
echo "op_name: $(($endt-$st)); $st -> $endt"

I added a bunch of blocks like the one above to my zshrc. I sourced a few times. I kept adding and removing these blocks. I eliminated the sourcing of the 18 or so zsh files in my dotfiles -> They were all happening within the same second. I looked at the plugin loader, the definition and sourcing of some add-ons like travis and cargo, but they all happened very fast too.

The strange thing was: I couldn’t find the culprit. Every step that I profiled happened very fast, and YET, the whole process took nearly 6 seconds. What was going on here?

The Realization

I started searching for reasons and people running into this hurdle before. I had prezto in the back of my head, so I searched for that and comparisons too. That was when I reached this amazing article. The author put it quite succinctly:

Zsh is an amazing shell, … BUT. It’s God. Damn. Slow!

Those were my feelings too. I have used oh-my-zsh for nearly 5 years now, it has served me very well. It has helped me get through some tough times (I am almost certainly romanticising); But it was time.

I read another article. The steps were very simple. My configuration would work as is. The one thing that wouldn’t work would be the prompt. I would have to find a prompt I liked; Or, if it was un-obscure enough, recreate my beloved ys on prezto. (More about why I love this prompt so much in the Postscript)

The Switch

Having mulled over this enough, I decided to go for it. It was quite clear in my head, the only things that could really break were my prompt and my color scheme. Both things that couldbe reverted by cleaning everything up and making a new install of oh-my-zsh and trying another weekend. With that re-assurance, I copied my existing ~/.oh-my-zsh into a backup folder, commited and pushed everything in my dotfiles and began.

git checkout -b switch-to-prezto

The process was fairly simple. After uninstall_oh_my_zsh and installing prezto using the commands on the readme, I had a running shell. My zshrc was working well. Perhaps the thing that most surprised me was that the color scheme was unaffected! I have dealt with fixing the color scheme before and it was incredibly obscure, undocumented and painful. So, that is one thing I was extremely grateful for.

Having ensured that the shell itself worked without any of the stuff on top of it, I started looking at the list of plugins (prezto calls them modules) that I wanted. I ensured that I was getting fish-like auto-suggestions before I even contemplated this move because that is one of the most useful things EVER! (It’s so incredibly fast; compared to even fzf)

I included the ones that I absolutely needed and auto-suggestions and syntax-highlighting started working! It was surprisingly easy and in fact, easier than oh-my-zsh which is so popular because it is easy and versatile. I had quite a bit of trouble setting up the right colors in oh-my-zsh with auto-suggestions because the color has to be dimmer than the color you are typing in but at the same time, it has to have enough contrast with the terminal background to ensure that you can see the suggestion without straining your eyes. I had hit the right balance with this with a slight change on top of solarized-dark.256 and I didn’t want to go through setting this up again. Thankfully, prezto worked fine the first time!

At this point, I had a working shell with all my aliases and functions; vim mode in the shell was working fine too. The experience till now had been good. The one thing left to configure: The Prompt.

The Prompt

TL; DR I couldn’t find the prezto equivalent for ys, so I wrote a prompt that looks exactly like ys.

I like ys because it doesn’t have any crazy characters. The green “o” looks just as gratifying as the green tick mark. The red “x” is almost indistinguishable from the red UTF8 “x” mark. One of the main reasons I chose ys was because it has only ASCII characters. That ensures that it looks good everywhere: Ubuntu, Mac OS, even Windows would have worked fine (if I had ever tried to make it work there). There was no ys clone for prezto, which was quite surprising; I was confident I would be able to get away copying some text around without having to understand how prompts are written. Hard luck there!

So, I started writing this prompt. I copied ys’s code over to a file which I thought would make it work. It was actually surprisingly close to what I wanted. The colors were off because it seems there’s a slight difference in how colors are formatted in oh-my-zsh prompts and prezto prompts:

After changing this around, things started looking very similar! In fact, the only thing left was the git-related info in the prompt.

At this point, I realized that I would have to go deeper into the prompt world or figure out the correct piece of code to copy from an existing prompt module. I couldn’t make the latter option work, so I decided to delve deeper into the prompt world.

Reading all of those READMEs and looking at some of the existing prompt files, I figured out how the flow of prompt files works. And ended up creating the exact same thing that I had on oh-my-zsh! And I’m mighty proud of that!

The Result

At long last, I had reached the point where my prompt looked and worked as I wanted it to. It was time for the test:

$ date +%s; source ~/.zshrc; date +%s

For some reason, the above command wouldn’t print the time after loading the shell. So, I measured the time to start a shell by opening a tmux pane or an iTerm2 tab: 2 seconds!

That’s 4 seconds saved every time I decide to open a new pane in tmux!

My fork of prezto is here. The prezto equivalent of ys is here.

This is a completely new shell and I am surprised at how similar it feels to oh-my-zsh and I don’t feel like I have to make any more changes, already. I might make some minor changes though, because using the git module brings in a bunch of git-related aliases that I don’t particularly like having around. (Eg: gcR removes the top commit from your branch. It’s a soft reset and is probably useful, so I might keep that one around. But there are a bunch of others listed here that I don’t like having around)

Postscript

This has been the third project that I have done something real on which will not get merged into the main project but will only be used by me. I am sort of having a re-realization about how powerful open-source software is. These are all fairly small projects. Being able to make these small changes in them to make your workflow a lot faster and also give you the satisfaction of understanding the tools you use on a daily basis is pretty empowering. All you really need to be able to do this kind of thing is a some know-how about the language that the tool is written in, the time to do it and the motivation to.

Doing this on a small scale, also kind of makes me wonder about the power of being able to do this at a much larger scale: an embedded software engineer’s ability to make changes in the http library or a Linux kernel developer’s ability to tweak things to be just so.