I'm using zsh
version 5.1.1
and tmux
version 2.3
, and recently I've noticed that starting a new shell (via a new window or pane in tmux
) takes more time than before.
It's enough for me to type around 5 characters before the prompt is displayed. I would like to reduce this time, so I wrote a return
statement at various positions inside my zshrc
file until I found the line which was responsible for the increased starting time. It seems to be:
fpath=(~/.zsh/completion $fpath)
And I can reproduce my issue with the following minimal zshrc
:
fpath=(~/.zsh/completion $fpath)
autoload -Uz compinit
compinit
The first line prepends the path ~/.zsh/completion
to the array fpath
. I do this because I want to write the files containing the code of my custom completion functions inside this directory.
To get a sense of how much time it takes for a shell to start, I found this command:
for i in $(seq 1 10); do /usr/bin/time zsh -i -c exit; done
It starts and exits 10 shells consecutively, each time measuring the time it took. Here's what it reports with the previous minimal zshrc
:
0.36user 0.04system 0:00.42elapsed 96%CPU (0avgtext+0avgdata 6056maxresident)k
0inputs+160outputs (0major+9087minor)pagefaults 0swaps
0.30user 0.04system 0:00.36elapsed 96%CPU (0avgtext+0avgdata 5900maxresident)k
0inputs+160outputs (0major+8949minor)pagefaults 0swaps
0.30user 0.05system 0:00.37elapsed 96%CPU (0avgtext+0avgdata 6080maxresident)k
0inputs+160outputs (0major+8992minor)pagefaults 0swaps
0.31user 0.04system 0:00.37elapsed 95%CPU (0avgtext+0avgdata 5936maxresident)k
0inputs+160outputs (0major+8956minor)pagefaults 0swaps
0.30user 0.04system 0:00.36elapsed 96%CPU (0avgtext+0avgdata 6052maxresident)k
0inputs+160outputs (0major+9089minor)pagefaults 0swaps
0.32user 0.04system 0:00.38elapsed 96%CPU (0avgtext+0avgdata 5944maxresident)k
0inputs+160outputs (0major+8948minor)pagefaults 0swaps
0.32user 0.04system 0:00.37elapsed 95%CPU (0avgtext+0avgdata 6004maxresident)k
0inputs+160outputs (0major+8991minor)pagefaults 0swaps
0.32user 0.02system 0:00.36elapsed 96%CPU (0avgtext+0avgdata 6056maxresident)k
0inputs+160outputs (0major+9109minor)pagefaults 0swaps
0.30user 0.05system 0:00.36elapsed 96%CPU (0avgtext+0avgdata 6072maxresident)k
0inputs+160outputs (0major+9003minor)pagefaults 0swaps
0.31user 0.04system 0:00.36elapsed 96%CPU (0avgtext+0avgdata 6040maxresident)k
0inputs+160outputs (0major+9055minor)pagefaults 0swaps
I'm not sure to read this correctly, but it seems to indicate that on average a shell needs 0.32
second to start.
Here's what the same command produces with the same minimal zshrc
after commenting the line fpath=(...)
:
0.08user 0.02system 0:00.10elapsed 97%CPU (0avgtext+0avgdata 5608maxresident)k
0inputs+0outputs (0major+1721minor)pagefaults 0swaps
0.06user 0.00system 0:00.07elapsed 97%CPU (0avgtext+0avgdata 5660maxresident)k
0inputs+0outputs (0major+1723minor)pagefaults 0swaps
0.05user 0.00system 0:00.06elapsed 92%CPU (0avgtext+0avgdata 5680maxresident)k
0inputs+0outputs (0major+1720minor)pagefaults 0swaps
0.05user 0.00system 0:00.06elapsed 95%CPU (0avgtext+0avgdata 5724maxresident)k
0inputs+0outputs (0major+1734minor)pagefaults 0swaps
0.04user 0.02system 0:00.06elapsed 95%CPU (0avgtext+0avgdata 5748maxresident)k
0inputs+0outputs (0major+1730minor)pagefaults 0swaps
0.05user 0.00system 0:00.06elapsed 95%CPU (0avgtext+0avgdata 5692maxresident)k
0inputs+0outputs (0major+1724minor)pagefaults 0swaps
0.04user 0.01system 0:00.06elapsed 95%CPU (0avgtext+0avgdata 5636maxresident)k
0inputs+0outputs (0major+1728minor)pagefaults 0swaps
0.04user 0.01system 0:00.06elapsed 95%CPU (0avgtext+0avgdata 5628maxresident)k
0inputs+0outputs (0major+1727minor)pagefaults 0swaps
0.04user 0.01system 0:00.06elapsed 95%CPU (0avgtext+0avgdata 5684maxresident)k
0inputs+0outputs (0major+1722minor)pagefaults 0swaps
0.05user 0.00system 0:00.06elapsed 95%CPU (0avgtext+0avgdata 5728maxresident)k
0inputs+0outputs (0major+1731minor)pagefaults 0swaps
Now, on average, it seems to take only 0.05
second.
By default, the value of FPATH
is:
/usr/local/share/zsh/site-functions:/usr/share/zsh/vendor-functions:/usr/share/zsh/vendor-completions:/usr/share/zsh/functions/Calendar:/usr/share/zsh/functions/Chpwd:/usr/share/zsh/functions/Completion:/usr/share/zsh/functions/Completion/AIX:/usr/share/zsh/functions/Completion/BSD:/usr/share/zsh/functions/Completion/Base:/usr/share/zsh/functions/Completion/Cygwin:/usr/share/zsh/functions/Completion/Darwin:/usr/share/zsh/functions/Completion/Debian:/usr/share/zsh/functions/Completion/Linux:/usr/share/zsh/functions/Completion/Mandriva:/usr/share/zsh/functions/Completion/Redhat:/usr/share/zsh/functions/Completion/Solaris:/usr/share/zsh/functions/Completion/Unix:/usr/share/zsh/functions/Completion/X:/usr/share/zsh/functions/Completion/Zsh:/usr/share/zsh/functions/Completion/openSUSE:/usr/share/zsh/functions/Exceptions:/usr/share/zsh/functions/MIME:/usr/share/zsh/functions/Misc:/usr/share/zsh/functions/Newuser:/usr/share/zsh/functions/Prompts:/usr/share/zsh/functions/TCP:/usr/share/zsh/functions/VCS_Info:/usr/share/zsh/functions/VCS_Info/Backends:/usr/share/zsh/functions/Zftp:/usr/share/zsh/functions/Zle
At the beginning of the variable, I noticed the path /usr/local/share/zsh/site-functions
.
It was an empty directory, so I removed it and replaced it with a symbolic link whose target is my completion directory:
cd /usr/local/share/zsh; sudo rm site-functions; sudo ln -s ~/.zsh/completion site-functions
With this symbolic link, I don't need to change the value of fpath
.
The completion functions still work, and the starting time of the shells has been divided by 4
, from around 0.32
second to around 0.08
second. The difference is noticeable to me. However, it doesn't feel right to use a system-wide directory.
Is there another, better, way to achieve the same result?
Edit:
Thanks to @thrig, I've realised that my ~/.zsh/
directory was in fact a symbolic link to a Dropbox directory:
ls -l .zsh
lrwxrwxrwx 1 user user 16 jan. 18 18:54 .zsh -> Dropbox/conf/zsh
I did this to have a backup of my configuration, and because I don't know how to use git.
If I move the completion functions to a directory outside Dropbox, zsh
starting time becomes much lower.
Edit 2:
I don't understand, now it seems zsh
starts slowly again, even if I put my completion functions outside of the Dropbox directory.
In case it helps, I have uploaded on a pastebin-like site the output of the following command:
strace -e trace=desc -o log -r zsh
According to man strace
, the -e trace=desc
option should trace all file descriptor related system calls. And the -r
option should print a relative timestamp upon entry to each system call.
Here's a link for the log when fpath
is unchanged.
And here's a 2nd link for the log when fpath
is changed.
I'm unable to interpret the log files, however there's a big difference in size. The first one seems to include around only 4000
system calls, while the 2nd one has around 67000
.
Edit 3:
I also recorded the output of the following command:
PS4='+[%D{%H:%M:%S.%.}]%N:%i> ' zsh -x
… with the script
utility.
Here's a link to the log file when fpath
is unchanged.
And here's a link to the log file when fpath
is changed.
This time, the first log file has only around 600
lines, while the second one has around 100000
lines.
In the log file where fpath
is changed, the functions which are called most often are compdef
(around 69000
times), compinit
(around 16000
times), and compdump
(around 14000
times).
Thank you for all the help given in the comments.
repeat 10 =time zsh -i -c exit
on ZSH 5.3.1 on Centos7. How many files do you have in~/.zsh/completion
?~/.zsh/completion
. However, your comment helped me understand that it wasn't a problem with myzshrc
, but with the directory itself. I've just realised that in fact, it was a symlink to a directory stored inside Dropbox. If I move the completion functions to a directory outside Dropbox, the times get much lower. I should probably stop using Dropbox. I will once I know how to better use git. It's weird though, myzshrc
is also symlinked to a Dropbox file, and it doesn't seem to cause any issue.strace -e trace=desc -o blah -r zsh ...
for Dropbox versus not loads? This should trace file descriptor related calls and print relative timestamps.PS4='+[%D{%H:%M:%S.%.}]%N:%i> ' zsh -x
to profile your shell startup. (maybe run underscript
to save the output)