如何减少更改`fpath`数组的值对zsh启动时间的影响?

如何减少更改`fpath`数组的值对zsh启动时间的影响?

我正在使用zshversion5.1.1tmuxversion 2.3,最近我注意到启动一个新的 shell(通过 中的新窗口或窗格tmux)比以前需要更多的时间。

在显示提示之前输入大约 5 个字符就足够了。我想减少这个时间,所以我在文件return中的各个位置写了一条语句,zshrc直到找到导致启动时间增加的行。好像是:

fpath=(~/.zsh/completion $fpath)

我可以用以下最少的内容重现我的问题zshrc

fpath=(~/.zsh/completion $fpath)
autoload -Uz compinit
compinit

~/.zsh/completion第一行在数组前面添加路径fpath。我这样做是因为我想在此目录中写入包含自定义完成函数代码的文件。

为了了解 shell 启动需要多长时间,我找到了以下命令:

for i in $(seq 1 10); do /usr/bin/time zsh -i -c exit; done

它连续启动和退出 10 个 shell,每次测量所花费的时间。这是它用之前的最小值报告的内容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

我不确定是否正确阅读了此内容,但它似乎表明 shell 平均需要0.32第二秒才能启动。

zshrc以下是注释该行后,相同的命令以相同的最小值生成的结果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

现在,平均而言,似乎只需要0.05第二名。

默认情况下, 的值为FPATH

/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

在变量的开头,我注意到了路径/usr/local/share/zsh/site-functions
这是一个空目录,所以我删除了它并用一个符号链接替换它,其目标是我的完成目录:

cd /usr/local/share/zsh; sudo rm site-functions; sudo ln -s ~/.zsh/completion site-functions

有了这个符号链接,我不需要更改 的值fpath。完成功能仍然有效,并且 shell 的启动时间已除以4,从大约0.32秒到大约0.08秒。这种差异对我来说是显而易见的。但是,使用系统范围的目录感觉不太合适。

还有另一种更好的方法可以达到相同的结果吗?


编辑:

谢谢@thrig,我意识到我的~/.zsh/目录实际上是 Dropbox 目录的符号链接:

ls -l .zsh
lrwxrwxrwx 1 user user 16 jan. 18 18:54 .zsh -> Dropbox/conf/zsh

我这样做是为了备份我的配置,并且因为我不知道如何使用 git。

如果我将补全功能移至 Dropbox 之外的目录,zsh启动时间会大大缩短。


编辑2:

我不明白,现在它似乎zsh再次开始缓慢,即使我将完成功能放在 Dropbox 目录之外。

如果有帮助,我已将以下命令的输出上传到类似 Pastebin 的网站上:

strace -e trace=desc -o log -r zsh

根据man strace,该-e trace=desc选项应该跟踪所有与文件描述符相关的系统调用。该-r选项应在进入每个系统调用时打印相对时间戳。

这是日志的链接fpath没有改变
这是日志的第二个链接fpath被改变了

我无法解释日志文件,但大小有很大差异。第一个似乎仅包含4000系统调用,而第二个则包含67000.


编辑3:

我还记录了以下命令的输出:

PS4='+[%D{%H:%M:%S.%.}]%N:%i> ' zsh -x

...与script实用程序。

这是日志文件的链接fpath没有改变
这是日志文件的链接fpath被改变了

这次,第一个日志文件仅包含大约600行,而第二个日志文件包含大约100000行。

在发生更改的日志文件中fpath,最常调用的函数是compdef(around 69000times)、compinit(around 16000times) 和compdump(around 14000times)。

感谢您在评论中提供的所有帮助。

答案1

我认为问题可能出在我的 zsh 版本上,所以我清除了zsh包并从源代码编译它:

% sudo aptitude install git-core gcc make autoconf yodl libncursesw5-dev texinfo

% git clone git://zsh.git.sf.net/gitroot/zsh/zsh
% cd zsh
% git checkout zsh-5.3.1

% ./Util/preconfig

% ./configure --build=x86_64-linux-gnu \
--prefix=/usr \
--includedir=/usr/include \
--mandir=/usr/share/man \
--infodir=/usr/share/info \
--sysconfdir=/etc \
--localstatedir=/var \
--libdir=/usr/lib/x86_64-linux-gnu \
--libexecdir=/usr/lib/x86_64-linux-gnu \
--bindir=/bin \
LDFLAGS="-Wl,--as-needed -g" \
--enable-maildir-support \
--enable-etcdir=/etc/zsh \
--enable-function-subdirs \
--enable-site-fndir=/usr/local/share/zsh/site-functions \
--enable-fndir=/usr/share/zsh/functions \
--with-tcsetpgrp \
--with-term-lib="ncursesw tinfo" \
--enable-cap \
--enable-pcre \
--enable-readnullcmd=pager \
--enable-custom-patchlevel=Debian \
--enable-additional-fpath=/usr/share/zsh/vendor-functions,/usr/share/zsh/vendor-completions \
--disable-ansi2knr

INSTALL我通过阅读文件以及通过 Google 找到的要点找到了依赖项和配置选项:

https://gist.github.com/nicoulaj/715855

...并通过查看 Ubuntu 开发人员如何编译最新的zsh软件包:

https://launchpadlibrarian.net/280509421/buildlog_ubuntu-yakkety-amd64.zsh_5.2-5ubuntu1_BUILDING.txt.gz

有一些选项写了两次,或者zsh无法识别,所以我删除了它们。 Ubuntu 开发者给出的值LDFLAGS似乎在我的机器上不起作用,所以我从 Github 上的 gist 复制了一个。我保留的选项描述如下./configure --help

--build=BUILD           configure for building on BUILD [guessed]

--prefix=PREFIX         install architecture-independent files in PREFIX
                        [/usr/local]

--includedir=DIR        C header files [PREFIX/include]

--mandir=DIR            man documentation [DATAROOTDIR/man]

--infodir=DIR           info documentation [DATAROOTDIR/info]

--sysconfdir=DIR        read-only single-machine data [PREFIX/etc]

--localstatedir=DIR     modifiable single-machine data [PREFIX/var]

--libdir=DIR            object code libraries [EPREFIX/lib]

--libexecdir=DIR        program executables [EPREFIX/libexec]

--bindir=DIR            user executables [EPREFIX/bin]

 LDFLAGS                linker flags, e.g. -L<lib dir> if you have libraries in a
                            nonstandard directory <lib dir>

--enable-maildir-support
                        enable maildir support in MAIL and MAILPATH

--enable-etcdir=DIR     the default directory for global zsh scripts

--enable-function-subdirs
                        install functions in subdirectories

--enable-site-fndir=DIR same for site functions (not version specific)

--enable-fndir=DIR      the directory in which to install functions

--with-tcsetpgrp        assumes that tcsetpgrp() exists and works correctly

 --with-term-lib=LIBS   search space-separated LIBS for terminal handling

 --enable-cap           enable the search for POSIX capabilities (may
                        require additional headers to be added by hand)

--enable-pcre           enable the search for the pcre library (may create
                        run-time library dependencies)

--enable-readnullcmd=PAGER
                        pager used when READNULLCMD is not set

--enable-custom-patchlevel
                        set a custom ZSH_PATCHLEVEL value

--enable-additional-fpath=DIR
                        add directories to default function path

--enable-ansi2knr       translate source to K&R C before compiling

编译成功,但该make check命令执行的 48 次测试中有 2 次失败:

% make
% make check
**************************************
46 successful test scripts, 2 failures, 0 skipped
**************************************
Makefile:187: recipe for target 'check' failed
make[1]: *** [check] Error 1
make[1]: Leaving directory '/home/user/GitRepos/zsh/Test'
Makefile:263: recipe for target 'check' failed
make: *** [check] Error 2

我无法摆脱他们。

最后,我以前没有使用它make install来安装二进制文件,而是checkinstall使用了一个.deb,如果将来需要更改 shell,我可以将其删除(使用dpkg -r zsh):

% sudo checkinstall

在安装过程中,我必须给出一个简短的描述(我使用了shell with lots of features),更重要的是我必须给出一个版本。如果没有与 Debian 政策兼容的版本, checkinstall将不会生成.deb.我查看了 的输出apt-cache policy zsh以检查 Debian 使用的命名方案是什么,并选择了5.3.1-1ubuntu2

% echo /bin/zsh >> /etc/shells
% chsh  →  /bin/zsh

这两行对于让 Ubuntu 识别/bin/zsh为有效的登录 shell 并使其成为我的默认 shell 是必要的。

现在,shell 版本是5.3.1(而不是5.1.1):

% zsh --version
    zsh 5.3.1 (x86_64-pc-linux-gnu)

shell 启动很快(大约0.04s):

% repeat 10 =time zsh -i -c exit

    0.06user 0.03system 0:00.12elapsed 80%CPU (0avgtext+0avgdata 5592maxresident)k
    0inputs+0outputs (0major+5668minor)pagefaults 0swaps
    0.06user 0.01system 0:00.09elapsed 77%CPU (0avgtext+0avgdata 5480maxresident)k
    0inputs+0outputs (0major+5634minor)pagefaults 0swaps
    0.04user 0.01system 0:00.07elapsed 82%CPU (0avgtext+0avgdata 5736maxresident)k
    0inputs+0outputs (0major+5641minor)pagefaults 0swaps
    0.04user 0.01system 0:00.07elapsed 77%CPU (0avgtext+0avgdata 5652maxresident)k
    0inputs+0outputs (0major+5645minor)pagefaults 0swaps
    0.03user 0.03system 0:00.07elapsed 82%CPU (0avgtext+0avgdata 5736maxresident)k
    0inputs+0outputs (0major+5634minor)pagefaults 0swaps
    0.04user 0.01system 0:00.07elapsed 78%CPU (0avgtext+0avgdata 5548maxresident)k
    0inputs+0outputs (0major+5624minor)pagefaults 0swaps
    0.04user 0.02system 0:00.07elapsed 78%CPU (0avgtext+0avgdata 5612maxresident)k
    0inputs+0outputs (0major+5655minor)pagefaults 0swaps
    0.04user 0.01system 0:00.07elapsed 77%CPU (0avgtext+0avgdata 5780maxresident)k
    0inputs+0outputs (0major+5624minor)pagefaults 0swaps
    0.03user 0.02system 0:00.07elapsed 78%CPU (0avgtext+0avgdata 5584maxresident)k
    0inputs+0outputs (0major+5641minor)pagefaults 0swaps
    0.03user 0.02system 0:00.07elapsed 78%CPU (0avgtext+0avgdata 5668maxresident)k

第一次编译的时候,我忘记切换到最新的release分支,所以就在master.
这个开发版本的zsh启动时间比 Ubuntu 存储库中的版本还要糟糕。左右0.42s启动一个shell。但同样,只有当我向fpathin添加一个目录zshrc,并在后者中创建一个文件时(一个简单的文件touch file就足够了)。

所以我想知道问题是来自编译选项,还是来自发行版本。我希望我编译的发行版本能够保持快速......

谢谢@thright@斯特凡·查泽拉斯在评论中帮助我。

相关内容