I have recently switched my machine at work from Linux to Window (for many reasons that are beyond this post). And to keep using some of the tools I’m familiar with, I turned to WSL (Windows Subsystem for Linux). It is reasonably good for the most part¹. One thing that was not so good, was the initialization time of zsh, which was taking roughly 20s when opening on WSL. I have used zprof in the past to identify why zsh was taking so long to start on my Linux machine, so I did the same now for my WSL setup.

To my surprise, what was taking the most time, was initializing the completions (compinit) and it’s subsequent calls (810x compdef):

num  calls                time                       self            name
-----------------------------------------------------------------------------------
 1)    2        3735.87  1867.94   52.48%   1313.69   656.84   18.45%  compinit
 2)  810        1103.52     1.36   15.50%   1103.52     1.36   15.50%  compdef
 3)  ...

I’m not entirely sure on why this happens on WSL only (as I have the same dotfile in my Linux); but I did find other people that were facing a similar issue on “slow systems” and suggested to use .zcompdump to backup and restore the results of compinit once a day.

The Solution

Similar to gist above, I attempted to cache the results into a file and load them whenever opening zsh. If there is already a file for today, then use it.

Here’s how I’ve done it:

current=$ZSH_CONFIG/.zcompdump-$(date '+%Y%m%d')
if [ -f $current ]; then
	compinit -C $current
else
	compinit -d $current 
fi

Note that $ZSH_CONFIG is my zsh config folder. This could be changed by $HOME or whatever other folder fits your needs.

Adding a check to environment

First, I don’t know exactly all the side effects of managing my own .zcompdump. I imagine that if any new completions are added given any package I installed, I would have to run this to make sure the completions are regenerated. For this reason, I’m adding a check to use this workaround only on WSL.

This answer from stackoverflow did the tick for me.

Here’s how the code looks like³:

# Determine if on WSL
if grep -q Microsoft /proc/version; then
    ON_WSL=1
else
    ON_WSL=0
fi

#...
autoload -Uz compinit
if [[ $ON_WSL == 0 ]]; then 
    compinit
else
    # Save completion to cache since it takes too much time to load on WSL
    current=$ZSH_CONFIG/.zcompdump-$(date '+%Y%m%d')
    if [ -f $current ]; then
		compinit -C $current
	else
		compinit -d $current 
	fi
fi

Removing old dumps

Another idea is to remove any previous dumps in case zsh needs to generate a new one, to avoid cluttering the folder with many .zcompdump files for each day I’ve logged in my computer. This can be done by removing all files matching .zcompdump-* before creating the new file dump file.

current=$ZSH_CONFIG/.zcompdump-$(date '+%Y%m%d')
if [ -f $current ]; then
	compinit -C $current
else
	rm $ZSH_CONFIG/.zcompdump-* 2>/dev/null
	compinit -d $current 
fi

Plus, add a 2>/dev/null to redirect all error output to null in case no .zcompdump files were created and avoid having weird error messages when opening zsh³.

Conclusion

I’ve been trying to do weekly revisions of my development environment, sometimes it’s on my personal setup and sometimes on my work machine. It’s kind of challenging since I have a Linux for personal projects and any Open Source contribution and (now) a Windows machine for my daily job. I don’t spent as much time as I wanted on Linux now but with WSL I have found some familiarity with the tools I was using in the past years.

In case you’re interested here’s the latest version of my .zshrc file.

Footnotes

¹ Unfortunately my company’s Windows image isn’t the latest so I’m still stuck with some rough around the edges version of WSL. Nonetheless most things are working fine.

² Initially, I had the $ON_WSL to be either set or unset, and checking it using if [ -z $ON_WSL]; but if (for any god forsaken reason) I assign this variable anywhere else (even to a negative value), of if my current environment sets it somewhere else, I’d have problems here. So I’m being explicit about the flag being on or off.

³ I might have found a bug on WSL while trying this. Redirecting error output does not work for glob. Running rm .zcompdump- &>/dev/null correctly redirects the error and nothing is shown on screen; but rm .zcompdump-* &>/dev/null shows the error zsh: no matches found: .zcompdump-* on the terminal. I didn’t look any further into this. Go figure.