LoginSignup
139
101

More than 3 years have passed since last update.

zshの起動が遅いのでなんとかしたい

Last updated at Posted at 2016-07-26

関連記事

概要

一言 : zshの起動が遅いのでなんとかしたい

tmuxでペインをどんどん作っては消したりしているとzshに起動の遅さでイライラしてくる。これは精神的に良くないのでできるだけ速くしてみた。

前提

dotfilesをあげているのでそれを見てくれると早いと思うがだいたい以下の様な流れで起動する。
環境はMac10.11、zsh5.2(brewでインストール)である。

  • zshrcから.zsh.d/*.zshを読み込む
  • プラグインマネージャーのzplugを使ってプラグインをロード
  • tmuxが立ちあがってなければtmuxを起動

また、oh-my-zshやpreztoなどのフレームワークは使わない。理由は以下のとおり。

  • 遅いと思われる。(実際試してないからなんとも言えないが…)
  • それに設定を自分で一度書いてしまったので移行するコストがめんどくさい。

時間の測定は以下のように、インタラクティブシェルの起動速度で測っている(終了する処理も含まれてしまうが)。

$ time ( zsh -i -c exit )

一回だけだとバラツキやすいので、以下のようにループするようにした。

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

高速化

zshの限界

そもそもzshはどれぐらいで起動するのか測ってみた。

$ : > /tmp/.zshrc
$ ( ZDOTDIR=/tmp/ zsh -i -c exit; )  0.00s user 0.01s system 65% cpu 0.012 total

空のzshrcで0.012秒。これがzshの限界のようだ。

現状

さて、チューニング前の起動速度を測ってみた。

( zsh -i -c exit; )  0.53s user 0.54s system 84% cpu 1.255 total

1.2秒かかっている。powerlineとか使ってたころはもっと遅かったと思うが、今ではこれでも遅いと感じてしまう。慣れとは恐ろしい。

プロファイル

とりあえず、なにがボトルネックとなっているのかzprofでプロファイリングしてみる。

.zshenvの最初と.zshrcの最後に以下を付け足す。

.zshenv
zmodload zsh/zprof && zprof
.zshrc
if (which zprof > /dev/null 2>&1) ;then
  zprof
fi

これでzshを再起動したらプロファイリングが出てくる。プロファイリングしないときは、.zshenvの1行をコメントアウトすればいい。

結果は以下のとおり

$ exec $SHELL -l
...
 1)    3         754.93   251.64   77.10%    417.11   139.04   42.60%  compinit
 2)    1         164.71   164.71   16.82%    164.71   164.71   16.82%  compdump
 3)    4         125.24    31.31   12.79%    124.28    31.07   12.69%  __parser__
 4)  751         119.53     0.16   12.21%    119.53     0.16   12.21%  compdef
 5)    4          53.78    13.44    5.49%     53.78    13.44    5.49%  compaudit
 6)    1         103.18   103.18   10.54%     34.33    34.33    3.51%  __zplug::zplug::cache::load
 7)    1          27.25    27.25    2.78%     27.03    27.03    2.76%  _zsh_highlight_load_highlighters
 8)    6         253.20    42.20   25.86%      8.66     1.44    0.88%  zplug

見方が自信ないけど

 1)    3(呼び出し回数)         754.93(合計実行時間)   251.64(1回あたりの実行時間)   77.10%    417.11(自分自身の合計実行時間)   139.04(自分自身の一回あたりの実行時間)    42.60%  compinit(関数名)

なのかな?
自分自身の実行時間というのはsourceなどで外部ファイルを読み込んだ時にかかる時間を除いた時間だろう。

ともあれ、compinitzplugが時間かかっている。compdefもかかっているように見えるが、今時zcompdumpを削除したため必要となっただけで、普段からする処理ではないので無視する。

これを元に、zsh.d以下のファイルをコメントアウトしていって、どれがボトルネックになっているのか探ってみた。
結果以下のファイルを読み込まないようにしたら62msで起動した。

  • 1develop.zsh : anyenvなどを読み込んでいる。
  • zplug_manager.zsh : プラグインマネージャーのzplugの設定、ロードをおこなっている。

compinit

compinitはzshの補完をするために必要なものなんで、おおよそ全てのzshrcに書かれているのではないかと思う。
しかし基本的に1回でいいので、3回も呼び出される必要はない。
最初はzplugでロードしたプラグインの中で呼び出されてるのかと思ったがzplugはなんとcompinitを一回だけ呼び出すようになっているらしい!すばらしい。

原因は1develop.zshで読み込んでいたBluemixのzsh_autocompleteの中でcompinitが呼ばれていたからだった。
該当部分をコメントアウトし、またzplugでcompinitが呼ばれるなら、自分で呼ぶ必要もないため.zshrcからもcompinitを消した。
これでcompinitが1回になった。

zplug

次にzplug。
zplugはプラグインマネージャーの中では早いが、200msぐらいはかかっているようだ。
結局プラグインは4つしかロードしていないのでマネージャーを使わない方が早いのかもしれないが、流石にそこまでするのはめんどくさい。
仕方ないので、未インストールのチェックを飛ばすことにした。

if ! zplug check --verbose; then
  printf "Install? [y/N]: "
  if read -q; then
    echo; zplug install
  fi
fi

上記の処理がzplugのサンプルでは書いてあると思うが、これをコメントアウトした。プラグインを追加した時、手動でインストールしないといけなくなるが、そう頻繁にあることではないし、まあいいかと思う。これで0.1s高速化した。

zgenも試してみたが、そんなに変わらなかった(170~220ms)。しかし、自動インストールも含めての速度なのでzgenの方が自動インストールを含めると早いということになる。zgenのチェックは6msぐらいしかかかっていないかったのでおそらくzplugとは違う方法で行っているのだろう。

まだ調べていないが、zplug特有の高速化もあるかもしれないので調べてみたい。

anyenv

1deveolp.zshの中でもeval "$(anyenv init - )"が処理の大半を占めていた。(zprofには出てこないのだが…)

time (eval "$(anyenv init - )")
( eval "$(anyenv init - )"; )  0.14s user 0.19s system 95% cpu 0.347 total

これは遅い!
とりあえず、--no-rehashをつけてみる。

time ( eval "$(anyenv init - --no-rehash)" )
( eval "$(anyenv init - --no-rehash)"; )  0.09s user 0.11s system 84% cpu 0.229 total

initで書き出しされるスクリプトを保存しておき、それを読み込むことで高速化できるようだが、anyenvだとめんどくさそう。
とりあえずanyenv init自体anyenv-initのラッパーなので実態を直接叩くことで少し高速化すると思う。

time (eval "$(env PATH="$ANYENV_ROOT/libexec:$PATH" $ANYENV_ROOT/libexec/anyenv-init - --no-rehash)")
( eval ; )  0.07s user 0.10s system 87% cpu 0.192 total

zcompile

今まで知らなかったのだが、zcompile.zshrcをコンパイルすることが出来る。これで外部ファイルの読み込みが速くなるはず。
変更した時自動でコンパイルされるように以下をzshrcに加えた。

if [ $DOTFILES/.zshrc -nt ~/.zshrc.zwc ]; then
  zcompile ~/.zshrc
fi

ただ、自分の環境だとあんまり変化はなかった。せいぜい0.03sほど速くなった気がする。

結果

time ( zsh -i -c exit )
Static loading...
( zsh -i -c exit; )  0.17s user 0.17s system 92% cpu 0.368 total

3倍ほど高速化した!
けどまだ少しラグを感じる。0.3sを切るとだいぶ早く感じるのであと0.1s頑張りたい。

追記(8/2)

一晩経ったら、0.5sまで遅くなっていた。おそらくcacheかなにかが効いていたのだろう。

anyenv 遅延ロード

やはりanyenvが遅いので少し手を加えた。
そもそもrbenvとかって毎回ロードする必要があるのだろうか?おそらく無いだろう。rbenvで設定したrubyが使えたら大抵の場合問題はない。
なので、rbenvなどは遅延ロードすることにした。

# anyenv
export ANYENV_ROOT="$(ghq root)/github.com/riywo/anyenv"
if [ -d $ANYENV_ROOT ]; then
  export PATH="$ANYENV_ROOT/bin:$PATH"
  for D in `command ls $ANYENV_ROOT/envs`
  do
    export PATH="$ANYENV_ROOT/envs/$D/shims:$PATH"
  done
fi

function anyenv_init() {
  eval "$(anyenv init - --no-rehash)"
}
function anyenv_unset() {
  unset -f ndenv
  unset -f rbenv
}
function ndenv() {
  anyenv_unset
  anyenv_init
  ndenv "$@"
}
function rbenv() {
  anyenv_unset
  anyenv_init
  rbenv "$@"
}

欠点は*envごとに関数を用意しないといけないこと。もはやanyenvの利点とは…
いっそことanyenvに組み込んだらいいのでは?という気がする。

追記:
上記の遅延ロードを行ったところ、npm install -gでグローバルにインストールしたパッケージがbinにエイリアスされなくなった。eval "$(ndenv init)"すれば治る。

結果

time ( zsh -i -c exit )
( zsh -i -c exit; )  0.16s user 0.12s system 99% cpu 0.273 total
❯ time (zplug load)
( zplug load; )  0.10s user 0.07s system 93% cpu 0.174 total

これでイライラは大分解消された!
ただ、たまにzplugのloadが遅い時があるのだが(0.6sほど)、zplugのcacheを使う条件はどうなっているのか調べてみたい。

16/10/18追記

手動でプロファイルしていると、PATHを設定しているenv_path.zshdevelop.zsh、aliasを設定しているalias.zshが不思議と時間かかっている。

そんなはずはないので原因を探してみたところ、パス解決にbrew --prefixghq rootを使っていたからのようだ。
これが一回あたり0.02~0.03sかかっているみたい。なのでこれを固定値に変えた。

今のところこんな感じ。

for i in $(seq 1 10); do time zsh -i -c exit; done
zsh -i -c exit  0.10s user 0.08s system 99% cpu 0.178 total
zsh -i -c exit  0.08s user 0.07s system 101% cpu 0.151 total
zsh -i -c exit  0.09s user 0.07s system 102% cpu 0.157 total
zsh -i -c exit  0.10s user 0.08s system 86% cpu 0.210 total
zsh -i -c exit  0.09s user 0.07s system 96% cpu 0.169 total
zsh -i -c exit  0.09s user 0.07s system 101% cpu 0.154 total
zsh -i -c exit  0.09s user 0.07s system 102% cpu 0.150 total
zsh -i -c exit  0.10s user 0.08s system 103% cpu 0.169 total
zsh -i -c exit  0.09s user 0.07s system 102% cpu 0.155 total
zsh -i -c exit  0.09s user 0.07s system 101% cpu 0.156 total

zplugが0.1~sかかっているので、これ以上求めるならプラグインを諦めないといけなさそう。

16/12/13追記

zplug諦めた

139
101
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
139
101