Shanoa Ice's Blog
16 y.o. high school student.


加快 Zsh 启动速度

Posted on

Bash 是个好 shell,但是我选择 Zsh,因为 Zsh 可配置性强,但可配置性强带来的后果就是你的 Zsh 可能会花上好几秒去启动,这样非常烦人,所以我现在就来分享一些加快 Zsh 启动速度的技巧

选择好的插件管理器

好的插件管理器是非常必要的,否则你可能会被缓慢的加载速度逼疯,这是比较推荐的插件管理器的列表:

  • oh-my-zsh: 插件管理器我首推 Oh My Zsh,尽管我现在并没有用它,但是它配置简单,而且自带大量实用插件,非常适合快速配置,但是注意不要加载太多插件,否则还是会拖慢启动速度。
  • antibody: 这是我最爱的一个插件管理器,它可能是所有插件管理器中最快的那一个了,在静态加载模式下几乎拥有着和手动加载一样的速度,而且能从几乎任何地方加载插件。

我并不推荐的管理器列表:

  • antigen: 它太臃肿,速度又慢的可怕,所以请不要使用它。
  • zplug: zplug 有着强大的功能,友好的用户界面,这两点我承认,但是它的加载速度实在令人难以置信。它需要花大致两倍于 antibody 的时间加载一个插件,所以也请不要使用它。

控制插件量

这没什么好讲的,不管是哪一种插件管理器,或者是原生加载方式,只要你加载太多插件,启动速度一定快不到哪里去。

挑选插件

某些插件会严重拖慢启动速度. 如果你用了速度快的插件管理器或者使用原生加载, 没有加载太多插件却依然卡顿的话那么你也许需要自行测试一下是哪个插件导致的. 找到问题以后, 如果这个插件并非必须, 那么你可以考虑不要加载它. 如果这个插件提供的功能是必需的, 那么你可以寻找一个快速的替代品.

懒加载命令行工具(Lazy-Loading)

这是一个比较高级的技巧. 像rbenv, pyenv这种需要在 shell 加载时初始化的工具可能会严重拖慢启动速度, 一种折中的解决方案就是 Lazy-Loading. 下面是一个例子:

rbenv() {
    eval "$(command rbenv init -)"
    rbenv "$@"
}

上面这段代码定义了一个函数, 覆盖了已有的 rbenv 命令. 这个函数的大致作用是先调用eval "$(command rbenv init -)"来初始化rbenv, 然后在把所有的参数原封不动地传给rbenv. 在你没有用到 rbenv 之前都不会执行初始化的任务, 因此也不会影响启动速度.

但是, 上面的方法有一点小小的问题, 就是你在调用被该工具管理的命令(例如, 对于rbenv, ruby命令)时不会加载该工具, 也执行不了那个工具管理的指定命令(例如, 当懒加载rbenv时, 不调用rbenv前调用ruby会执行系统安装的版本, 而不是rbenv管理的版本). 这个问题目前我还没有解决方案.

这就需要你做选择了, 更快的加载速度, 还是更方便的管理.

妙用函数

下面介绍的技巧其实是另一种形式的懒加载.

有些时候, 某些语言的包管理器不会为你自动配置PATH变量(例如nimnimble), 但是你确实需要从某些包中运行命令, 这可能配置起来很讨厌.

以我上面提到的nimble为例, nimble path <包名> 会打印指定包的安装路径. 有一个用于nim的构建工具叫做nake, 而它的可执行文件就在安装目录下, 此时通常的配置可能是这样的:

export PATH="$(nimble path nake):$PATH"

但是这样会在加载 zsh 时运行这条nimble path nake命令, 这可能会拖慢加载速度. 这里更好的解决方案是编写一个叫做nake的函数, 请看代码:

nake() {
    $(nimble path nake)/nake "$@"
}

这样加载 zsh 时就不会运行这条命令, 只在调用nake时运行.

测试加载速度

for i in $(seq 1 10); do /usr/bin/time -f "real %e  system %S  user %U" zsh -i -c exit; done

上面是一行用来测试zsh加载速度的命令, 总共执行十次, 显示每次的用时. 如果你想看看更详细的调试信息, 试试下面的命令:

zsh -i -c -x exit

这条命令的输出比较难懂, 这里我大致解释一下: 通常你只需要看每行的开头, 有命令/脚本名称和行号). 如果一个脚本或命令占用的行数很多或者等待时间较长的话, 你也许就要考虑这条命令是不是加载缓慢的罪魁祸首了.

参考