Skip to content

Instantly share code, notes, and snippets.

Last active May 5, 2023 08:57
What would you like to do?
Speed up zsh compinit by only checking cache once a day.
# On slow systems, checking the cached .zcompdump file to see if it must be
# regenerated adds a noticable delay to zsh startup. This little hack restricts
# it to once a day. It should be pasted into your own completion file.
# The globbing is a little complicated here:
# - '#q' is an explicit glob qualifier that makes globbing work within zsh's [[ ]] construct.
# - 'N' makes the glob pattern evaluate to nothing when it doesn't match (rather than throw a globbing error)
# - '.' matches "regular files"
# - 'mh+24' matches files (or directories or whatever) that are older than 24 hours.
autoload -Uz compinit
if [[ -n ${ZDOTDIR}/.zcompdump( ]]; then
compinit -C;
Copy link

aztack commented Dec 14, 2021

autoload -Uz compinit
for dump in ~/.zcompdump(; do
compinit -C

works for me.

Timing with timezsh:

timezsh() {
  for i in $(seq 1 4); do /usr/bin/time $shell -i -c exit; done


        1.33 real         0.80 user         0.52 sys
        1.38 real         0.80 user         0.57 sys
        1.55 real         0.82 user         0.64 sys
        1.50 real         0.81 user         0.63 sys


        0.78 real         0.32 user         0.42 sys
        0.81 real         0.33 user         0.45 sys
        0.96 real         0.37 user         0.55 sys
        1.21 real         0.42 user         0.71 sys

Copy link

xinxilas commented Mar 1, 2022

It was faster here

Definitive solution?

Copy link

vincentbernat commented Apr 7, 2022

I am going for

() {
    emulate -L zsh
    setopt extendedglob
    autoload -Uz compinit complist
    local zcd=$1                # compdump
    local zcdc=$1.zwc           # compiled compdump
    local zcda=$1.last          # last compilation
    local zcdl=$1.lock          # lock file
    local attempts=30
    while (( attempts-- > 0 )) && ! ln $zcd $zcdl 2> /dev/null; do sleep 0.1; done
        if [[ ! -e $zcda || -n $zcda( ]]; then
            compinit -i -d $zcd
            : > $zcda
            compinit -C -d $zcd
        [[ ! -f $zcdc || $zcd -nt $zcdc ]] && rm -f $zcdc && zcompile $zcd &!
    } always {
        rm -f $zcdl
} $ZSH/run/u/$HOST-$UID/zcompdump

I didn't see a mention what happens when multiple shells are spawned at the same time. Maybe that's the reason we sometime run into empty dump file.

Copy link

medwatt commented Apr 12, 2022

Where exactly do we put this code:

autoload -Uz compinit
for dump in ~/.zcompdump(; do
compinit -C

Copy link

faelin commented Jun 3, 2022

@medwatt put the snippet somewhere in your .zshrc file, or put it in an external script file and call it from your .zshrc

Copy link

@vincentbernat I solved the multiple shell problem by using a lockfile and a trap to remove it. This ensures that only one process is responsible for updating the file. Even though there's still a race condition, it's now almost impossibly small 1

() {
	setopt local_options
	setopt extendedglob

	local zcd=${1}
	local zcomp_hours=${2:-24} # how often to regenerate the file
	local lock_timeout=${2:-1} # change this if compinit normally takes longer to run
	local lockfile=${zcd}.lock

	if [ -f ${lockfile} ]; then 
		if [[ -f ${lockfile}(${lock_timeout}) ]]; then
				echo "${lockfile} has been held by $(< ${lockfile}) for longer than ${lock_timeout} minute(s)."
				echo "This may indicate a problem with compinit"
			) >&2 
		# Exit if there's a lockfile; another process is handling things
		# Create the lockfile with this shell's PID for debugging
		echo $$ > ${lockfile}
		# Ensure the lockfile is removed
		trap "rm -f ${lockfile}" EXIT

	autoload -Uz compinit

	if [[ -n ${zcd}(${zcomp_hours}) ]]; then
		# The file is old and needs to be regenerated
		# The file is either new or does not exist. Either way, -C will handle it correctly
		compinit -C
} ${ZDOTDIR:-$HOME}/.zcompdump 

For those who are new to some of this stuff, check out


  1. A second process would have to check for the file in between 2 (effectively) subsequent commands. if [ -f ${lockfile} ] and echo $$ > ${lockfile}

Copy link

@thefotios my solution is already handling this case (but my message wasn't quite clear on that, so I understand you thought this was not the case). This is the purpose of the ln command (which is atomic, so no race condition).

Copy link

rafpaf commented Dec 24, 2022

I commented out these lines in my .zshrc which sped it up a lot:

if type brew &>/dev/null; then
    FPATH=$(brew --prefix)/share/zsh-completions:$FPATH

    autoload -Uz compinit

As this comment above pointed out, oh-my-zsh already runs compinit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment