zsh 加速
这里主要是说一下最近针对开启速度的优化,具体执行速度我估计也不好整,众所周知,zsh 要是加上一些插件的话,再加上开发用到的一大堆环境的声明,最后的开启时间真的感人,常用的几种shell对比起来的话,fish,zsh,bash,这三个里面,zsh 可以说是最慢的,fish 很美好,但是其有时候语法和bash 不兼容,之后bash又有些难用,还是得回到zsh的怀抱,但是面对现在我这里几乎一两秒的开启速度,可见的打开延迟,让我非常糟心,之后思考,怎么优化这个速度。当然有人觉得1s 2s还慢?这就需要做一个简单的测试,看一看 bash 和 fish 的水平:
for i in $(seq 1 5); do /usr/bin/time fish -i -c exit; done
把中间的 fish 改一下就可以大致测试其他shell的速度,大致上是这样:
需要对 zsh 的开启速度进行优化,要不然这个开启速度对比太明显了。
zinit
大部分人会直接 oh-my-zsh 来直接搞,OMZ 太有名了,但是这样感觉不好管理插件,而且加载插件速度是比较慢的,需要一个管理器,一开始我使用的是 zplug,当时看到说是可以很快速的加载插件,大致原理好像是并行的启动的时候加载多个插件,不过后来在接触zinit之后发现,发现zplug好像实现的不大行,还是很慢,感觉有时候甚至比 OMZ 慢,zinit,虽然感觉使用的人少,但是发现它的实现算是非常好,使用另一种思路,打开shell的时候,肯定不会立刻用到所有插件,可以先不加载,只先加载基础功能,不要影响启动。zinit 的项目地址在这里,安装按照 readme 来就可以,下面是简单的介绍怎么使用,其实我也是用到了很小的一部分来加速启动。
首先来看在zinit中普通模式加载一个插件:
zinit light romkatv/zsh-defer
这样就加载了这个插件,如果需要加载 OMZ 插件,基本上是下面的样子:
zinit snippet OMZ::plugins/git/git.plugin.zsh
这就是基本的加载模式,如果每个插件都用这种加载方式,那么不会开启速度的提升,下面介绍 zinit 最有用的工作模式,也就是 turbo 模式,最基础的使用方式是下面这样:
zinit ice wait'!0'
zinit load halfo/lambda-mod-zsh-theme
这个的意思是在启动之后,也就是走完 zshrc 之后,0s 后立即(也就是立刻)开始加载这个插件,这样就可以将打开就需要的必要插件普通方式加载,而其他不太急需的插件都延后加载,这样就可以很大程度上降低开启时候的延迟,本质上没有说让加载过程变快,只是换用一种方式。但是这样写挺累的,zinit 给了一种 for 加载模式,长下面这个样子:
-
普通加载
zinit light-mode for \ zinit-zsh/z-a-rust \ zinit-zsh/z-a-as-monitor \ zinit-zsh/z-a-patch-dl \ zinit-zsh/z-a-bin-gem-node
这样就不用每次都重复写
zinit light
那么累了。 -
Turbo 模式
zinit wait lucid for \ zdharma/fast-syntax-highlighting \ mafredri/zsh-async \ OMZ::plugins/extract
这里的 lucid 我认为是指加载完后不提示信息,这里就是通过turbo模式加载多个插件的办法,把不是立刻就要使用的插件都用这种方式加载,而不是开启的时候就要全部加载完。
这大致上就是我使用到的部分,我没有特别深入的研究,这样操作之后,已经能很大程度上降低加载插件导致的速度问题。
最后说一下加载我喜欢的主题 pure 的方法:
zinit ice compile'(pure|async).zsh' pick'async.zsh' src'pure.zsh'
zinit light sindresorhus/pure
还是不够快
之后发现,怎么还不够快,其实这些就是环境变量等等东西背锅了,其中要点名批评 nvm,整个zsh 加载如果需要1s,nvm 加载能占 0.5s 多,几乎是破坏性的加载速度,但是我们还是需要用这种东西,就需要一种东西来让这些东西也延后加载一下,我没有仔细研究,zinit 怎么使用能够实现,我使用到了另一个插件,也就是上面说过加载方式的 zsh-defer 来实现我想要的功能,首先下用普通模式加载 zsh-defer,之后用conda举个例子:
conda_init_fun () {
# Add by conda
# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$('/Users/user/miniconda3/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
eval "$__conda_setup"
else
if [ -f "/Users/user/miniconda3/etc/profile.d/conda.sh" ]; then
. "/Users/user/miniconda3/etc/profile.d/conda.sh"
else
export PATH="/Users/user/miniconda3/bin:$PATH"
fi
fi
unset __conda_setup
# <<< conda initialize <<<
}
zsh-defer conda_init_fun
这里将所有的加载conda的语句放到一个函数中,之后让 zsh-defer 去加载,这样就不会导致卡在开启阶段没有一点反应了,虽然conda这样加载有后作用,因为conda需要在shell上修改,比如标示当前虚拟环境,这样加载之后会导致过一阵子shell才能输入文本,但是启动速度上就没有任何问题了。
类似这样的方法,可以将任何你想延后加载的脚本使用这种方式延后加载,让开启阶段变快,至少先进入到shell中,如果需要针对一句语句,可以这样操作:
zsh-defer -c '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"'
这样,暂时用不到的nvm就不至于在开启的时候卡住了,在特别极端的操作后,也就是几乎所有的包括环境变量,alias等,只要是打开shell阶段不会立即用到的,全部使用 turbo 模式或者是使用 zsh-defer 加载,开启速度达到了一个非常好的水平:
已经能够和 fish 一战,非常快的开启体验了,不过反作用就是开启的时候,我这里这样子激进的操作,开启的时候有很小的一段时间其实还是在加载东西,比如conda要加载出前面的 base
标示当前虚拟环境等,不过总比等白屏一点消息没有好。