Improve Your oh-my-zsh Startup Time (Maybe)

| Comments

Update 2012-10-17: The slow start up with the newer zsh package was my own fault. In my transition to Prezto, I unintentionally removed the skip_global_compinit=1 from my .zshenv file. The global start up scripts for zsh-beta don’t call compinit, but the newer ones in the zsh package do.

Update 2012-10-16: I’m using Prezto now, but I did notice something interesting today that probably still applies to oh-my-zsh. Ubuntu 12.04 has zsh and zsh-beta packages. Oddly, the zsh package is a more recent version than the zsh-beta package.

The older zsh-beta starts up almost five times faster:

$ /usr/bin/time /bin/zsh-beta -i -c exit
0.07user 0.04system 0:00.13elapsed 89%CPU
$ /bin/zsh-beta --version
zsh 4.3.12-dev-1-cvs (x86_64-unknown-linux-gnu)

$ /usr/bin/time /bin/zsh -i -c exit
0.47user 0.12system 0:00.64elapsed 92%CPU
$ /bin/zsh --version
zsh 4.3.17 (x86_64-unknown-linux-gnu)

For now I’m using zsh-beta, but I will have to investigate this further.

Update 2010-04-29: All of my changes to oh-my-zsh that are listed here were pulled into the master repository last night. Just about everything here can be ignored now except for the part about adding skip_global_compinit=1 to your ~/.zshenv if you are running Ubuntu (and possibly other distros). Adding that line still saves me almost a half second of startup time.

I’ve been very happy with zsh and oh-my-zsh except for one thing: the slow startup time. I’ve been putting up with a huge delay every time I open a new terminal, and I open terminals quite often. I did manage to solve the problem even with a total failure of my google-fu. To find the cause of the problem, I was in need of some sort of benchmark.

End of Updates

The benchmark

This turned out to be easy. I just ran /usr/bin/time zsh -i -c exit and took note of the elapsed time. The time to beat was an abysmal 1.65 seconds. That was 0.6 seconds slower than bash!

My first failure

First I tried to compile everything in my .oh-my-zsh directory using zcompile. It was a complete waste of time; there was no improvement what so ever.

My second failure

I started removing files from ~/.oh-my-zsh/lib and ~/.oh-my-zsh/custom. I couldn’t manage to get the startup time down below 1.2 seconds, and I was losing huge pieces of functionality. I was hoping I could pinpoint one or two particularly slow files to investigate, but that wasn’t the case.

Grasping at straws

I even tried moving my ~/.oh-my-zsh directory to a tmpfs partition. Of course this made no difference. The files were already cached anyway.

Getting to 0.49 seconds

I almost gave up. OK, I did give up. I didn’t find the solution until the next day…

The biggest slow down was in the system-wide zshrc file in /etc/zsh. On my Ubuntu 10.10 system the slow down is the call to compinit on the second-to-last line. Following the advice of the comment and adding skip_global_compinit=1 to my ~/.zshenv bought me over one full second.

Squeezing out a little more

There are calls to compinit in a few files in ~/.oh-my-zsh/lib and ~/.oh-my-zsh/plugins. I already have a call to compinit in one of my own scripts in ~/.oh-my-zsh/custom, so I tried commenting out the calls in the two occurrences in ~/.oh-my-zsh/lib (I don’t have any plugins enabled). Each one saved me about 0.07 seconds. That brings me down to 0.35 seconds, about 0.7 seconds faster than bash!

I’m not sure why oh-my-zsh has so many calls to compinit. I’m pretty sure every plugin calls it once. I haven’t noticed any changes in the way completion works, and I don’t think I will. As far as I can tell, compinit should only need to be called once.

Getting a bit more invasive

I removed all the calls to compinit, including the one in my own script in ~/.oh-my-zsh/custom. Then I added a single call to compinit to the end of the `~/.oh-my-zsh/’ script. This shaved off another 0.12 seconds, bringing my total down to 0.23 seconds. I don’t think there is any need for me to push things any more than this.