尝试提高 ZSH 的响应速度

Posted by Echo on October 1, 2016

事情的起因是某一天我突然发现,我的 zsh 怎么这么卡?

以前实在没在意 zsh 的响应速度,但是一旦注意到,就开始各种接受不了。

  • 打开一个 iTerm 标签页需要十几秒?
  • cd 一个目录需要一到两秒才有响应?
  • ls 一个目录要返回结果一两秒后才能操作?
  • ^C 结束进程反而卡在这不动?

我想到最近机器做过的变更实在太多。升级 macOS Sierra,iTerm 升级,oh-my-zsh 更新,实在有太多东西可能导致速度下降了。

然而 Google 实在搜不到相关升级会导致 zsh 卡顿的结果。

甚至我还想到可能是本子用的时间太长,ssd 读写速度下降导致的 zsh 卡顿。。。

我怎么用怎么觉得不爽,再也无心他事,直接扑在了改进 zsh 的速度上面。

我谷歌了很多资料,zsh 太慢的问题被很多人抱怨过,在此过程中我也学会了使用 zsh -xv 命令调试 zsh 。

我发现启动和运行过慢的原因有四点。

第一,加载过多了插件。

可能当我刚开始尝试用 oh-my-zsh 的时候,我参考了网上许多文章,加载了很多我用不到的插件。比如我同时开启了 z 和 autojump ,这两个插件的作用是一样的。在每次切换目录的时候,它们都会分别将切换的信息记入自己的数据库以进行权重统计,所以同时开启在一定程度上影响着 prompt 的响应速度。

在我关闭了许多自己用不到的插件之后, 明显速度提升了一些,但还不是非常明显。

第二,某些响应卡顿的命令明显影响 zsh 启动速度。

我使用zsh -xv 命令调试 zsh 的时候发现,每次启动当加载到了某些插件,比如 autojump,执行到brew --prefix命令的时候会有非常明显的卡顿。 而在加载过程中,不止一次会调用到这个命令。

所以我将所有插件中的brew --prefix都替换回了它的结果/usr/local,我觉得我并不会轻易改动 brew 的安装目录。修改之后,zsh 的启动时间从十几秒直接缩减到了 3.5 秒。

可以用下面这条命令测量 zsh 的启动时间。

usr/bin/time /bin/zsh -i -c exit

第三,所用主题的 prompt 功能会非常影响 prompt 的响应速度,尤其是 git prompt

我根据自己的使用习惯,在 agnoster 主题之上做了一些修改,添加了一些逻辑。在这次事件发生后,我读了一下主题的源码,做了一些优化。

比如,把类似 echo $var |sed ... 等造成多进程开销的命令都使用 Bash 内部替换代替(详见我翻译的这篇文章)。

而且这次我发现主题中的 git prompt 逻辑会非常影响 prompt 的响应速度。尤其是判断 git 中的版本有没有未提交的文件等状态的相关命令。我在测试的时候,将其替换成一个只显示 git 分支,不显示其他状态的 git prompt,其响应速度直接提升到让我满意的程度。

在这次 zsh 的卡顿事件中,在功能和性能之间,我最终做出了妥协,去掉了一些用不到功能和插件,换来一个我能接受的性能提升。

至于为什么不切换成速度更快的 fish,我考虑到我毕竟是做运维的,还是使用跟服务器上自带的 Bash 更相似一些的 zsh 为好。

这次我顺便还了解了 Antigen 、Antibody 等工具,和几个很实用、有意思的插件。

但是我还是没有使用 Antibody,我感觉它能支持的插件还是太少,当然这也可能是我对其了解的太浅显了。

最后为了缓解自己在 zsh 加载时候的焦虑心情,我做了一个 Loading 的命令行动画。

挺有意思的吧!