概要
zshの起動に1.5秒ほどかかっており、tmuxのpane立ち上げなどの待ち時間を短縮した。zsh/zrof
による計測を行いボトルネックになっていた nvm
, npm-completion
, compinit
の読み込みを修正した。
環境
- zsh 5.9 (arm-apple-darwin21.3.0)
ボトルネックの計測
zsh/zprof
モジュールが読み込まれた時点からシェルの関数呼び出しをプロファイリングし、 zprof
を呼び出すとプロファイルした結果が一覧で標準出力される。
したがって、zshのコンフィグ最初にモジュールを読み込み最後に zprof
を呼び出す。
.zshenv
でモジュールを読み込む。
# A module allowing profiling for shell functions.
zmodload zsh/zprof
if [ $? = 0 ]; then
zprof
fi
.zshrc
の行末で zprof
を実行する。
type zprof > /dev/null 2>&1
if [ $? = 0 ]; then
zprof
fi
zshを起動し、計測する。
$ time zsh -i -c exit
num calls time self name
-----------------------------------------------------------------------------------
1) 2 602.07 301.04 76.55% 324.99 162.49 41.32% nvm
2) 1 227.17 227.17 28.88% 194.41 194.41 24.72% nvm_ensure_version_installed
3) 1 736.58 736.58 93.65% 134.51 134.51 17.10% nvm_auto
4) 1 32.76 32.76 4.16% 32.76 32.76 4.16% nvm_is_version_installed
5) 1 49.65 49.65 6.31% 31.56 31.56 4.01% nvm_die_on_prefix
6) 4 26.30 6.57 3.34% 26.30 6.57 3.34% compaudit
7) 2 49.86 24.93 6.34% 23.56 11.78 3.00% compinit
8) 2 16.53 8.27 2.10% 16.53 8.27 2.10% nvm_grep
9) 4 18.08 4.52 2.30% 1.55 0.39 0.20% nvm_npmrc_bad_news_bears
10) 1 0.27 0.27 0.03% 0.27 0.27 0.03% nvm_has
11) 1 0.04 0.04 0.01% 0.04 0.04 0.01% compdef
12) 1 0.08 0.08 0.01% 0.04 0.04 0.00% complete
13) 1 736.60 736.60 93.65% 0.02 0.02 0.00% nvm_process_parameters
14) 1 0.01 0.01 0.00% 0.01 0.01 0.00% bashcompinit
15) 1 0.00 0.00 0.00% 0.00 0.00 0.00% nvm_is_zsh
...
real 0m1.443s
user 0m0.353s
sys 0m0.446s
起動時間にはばらつきがあるため、ボトルネックの調査ではなく全体の計測は複数回実行する。
for i in $(seq 1 10); do time zsh -i -c exit; done
結果から nvm
、 compaudit
, compinit
が速度の大半を占めていることがわかる。
nvm
.zshrc
にある nvm
の初期化を一旦コメントアウトするとシェルの立ち上げが早くなった。
Zsh の起動時間を短縮するを参考にnvmの初期化を遅延させる。
.zshrc
で行っている nvm
の処理は初期化とディレクトリ移動時に .nvmrc
を参照してバージョンを切り替えるスクリプトの2つ。
初期化処理を関数にまとめる。
load-nvm() {
# nvmが利用できなければ初期化する
if ! type nvm &>/dev/null; then
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
fi
}
ディレクトリ移動時に実行されるコマンドで load-nvm
を呼び出す。また、スクリプトでは関数をzshのフック登録後に実行しているが起動時にボトルネックになるので呼び出さない。
diff --git a/.zsh/path.zsh b/.zsh/path.zsh
index 8425cd8..0fc5798 100644
--- a/.zsh/path.zsh
+++ b/.zsh/path.zsh
@@ -28,6 +28,8 @@ load-nvm() {
+ load-nvm
local nvmrc_path="$(nvm_find_nvmrc)"
if [ -n "$nvmrc_path" ]; then
@@ -44,4 +46,5 @@ load-nvmrc() {
fi
}
add-zsh-hook chpwd load-nvmrc
-load-nvmrc
tmuxのpaneやwindowを立ち上げるなどして、シェルを初期化したディレクトリでは nvm
を利用できないがディレクトリを移動するか load-nvm
を呼び出せば良い。
nvm
関連の処理で1秒近くかかっていたので大きく短縮できた。
% time zsh -i -c exit
num calls time self name
-----------------------------------------------------------------------------------
1) 2 13.81 6.91 59.99% 13.81 6.91 59.99% compaudit
2) 1 22.89 22.89 99.41% 9.08 9.08 39.42% compinit
3) 1 0.14 0.14 0.59% 0.14 0.14 0.59% add-zsh-hook
-----------------------------------------------------------------------------------
2) 1 22.89 22.89 99.41% 9.08 9.08 39.42% compinit
1/2 13.81 13.81 59.99% 0.15 0.15 compaudit [1]
-----------------------------------------------------------------------------------
1/2 13.81 13.81 59.99% 0.15 0.15 compinit [2]
1/2 13.66 13.66 59.33% 13.66 13.66 compaudit [1]
1) 2 13.81 6.91 59.99% 13.81 6.91 59.99% compaudit
1/2 13.66 13.66 59.33% 13.66 13.66 compaudit [1]
-----------------------------------------------------------------------------------
3) 1 0.14 0.14 0.59% 0.14 0.14 0.59% add-zsh-hook
zsh -i -c exit 0.18s user 0.05s system 91% cpu 0.256 total
npm
pyenvの初期化を遅延呼び出しするを参考に、npm
コマンド実行時に一度だけ補完スクリプトを読み込むように修正した。
npm() {
unset -f npm
load-npm-completion
npm $@
}
npm
の補完を利用するためにはコマンドを一度実行する必要があるが、zsh初期化時に補完スクリプトを呼び出さないことで0.1秒ほど高速化した。
対応前
% for i in $(seq 1 10); do time zsh -i -c exit > /dev/null ; done
zsh -i -c exit > /dev/null 0.18s user 0.05s system 87% cpu 0.265 total
zsh -i -c exit > /dev/null 0.17s user 0.05s system 95% cpu 0.225 total
zsh -i -c exit > /dev/null 0.16s user 0.05s system 84% cpu 0.249 total
zsh -i -c exit > /dev/null 0.16s user 0.04s system 81% cpu 0.256 total
zsh -i -c exit > /dev/null 0.16s user 0.05s system 83% cpu 0.250 total
zsh -i -c exit > /dev/null 0.17s user 0.05s system 81% cpu 0.261 total
zsh -i -c exit > /dev/null 0.16s user 0.04s system 78% cpu 0.265 total
zsh -i -c exit > /dev/null 0.16s user 0.04s system 84% cpu 0.248 total
zsh -i -c exit > /dev/null 0.16s user 0.05s system 81% cpu 0.257 total
zsh -i -c exit > /dev/null 0.17s user 0.05s system 80% cpu 0.261 total
対応後
% for i in $(seq 1 10); do time zsh -i -c exit > /dev/null ; done
zsh -i -c exit > /dev/null 0.03s user 0.02s system 43% cpu 0.126 total
zsh -i -c exit > /dev/null 0.03s user 0.02s system 43% cpu 0.108 total
zsh -i -c exit > /dev/null 0.02s user 0.02s system 41% cpu 0.102 total
zsh -i -c exit > /dev/null 0.02s user 0.02s system 44% cpu 0.089 total
zsh -i -c exit > /dev/null 0.02s user 0.02s system 43% cpu 0.093 total
zsh -i -c exit > /dev/null 0.02s user 0.02s system 46% cpu 0.088 total
zsh -i -c exit > /dev/null 0.02s user 0.02s system 46% cpu 0.089 total
zsh -i -c exit > /dev/null 0.02s user 0.02s system 45% cpu 0.089 total
zsh -i -c exit > /dev/null 0.02s user 0.02s system 44% cpu 0.092 total
zsh -i -c exit > /dev/null 0.02s user 0.02s system 43% cpu 0.095 total
compaudit
補完システムに必要なディレクトリで危険なファイルが含まれないか所有権の確認などのセキュリティチェックを行う。シェルから実行可能だが、 compinit
実行時に呼び出される。 compinit
に -C
オプションが指定され、ダンプファイルが存在していればセキュリティチェックはスキップされる。
For security reasons compinit also checks if the completion system would use files not owned by root or by the current user, or files in directories that are world- or group-writable or that are not owned by root or by the current user. If such files or directories are found, compinit will ask if the completion system should really be used. To avoid these tests and make all files found be used without asking, use the option -u, and to make compinit silently ignore all insecure files and directories use the option -i. This security check is skipped entirely when the -C option is given, provided the dumpfile exists.
compdump
compinitは読み込んだ設定のダンプを .compdump
に出力し、次回以降の compinit
の実行を高速化する。
compinit
compinit
を呼び出すことで補完システムをセットアップする。 compinit
実行時に呼び出される compaudit
の処理時間が次のボトルネックとなったため対応した。対応としては compinit
実行時に出力されるダンプファイル .zcompdump
の作成から一定時間経過していなければ compaudit
をスキップする。
対応はctechols/compinit.zshを参考にした。
local now=$(date +"%s")
local updated=$(date -r ~/.zcompdump +"%s")
local threshold=$((60 * 60 * 24))
if [ $((${now} - ${updated})) -gt ${threshold} ]; then
compinit
else
# if there are new functions can be omitted by giving the option -C.
compinit -C
fi
% time zsh -i -c exit
num calls time self name
-----------------------------------------------------------------------------------
1) 1 11.65 11.65 98.12% 11.65 11.65 98.12% compinit
2) 1 0.22 0.22 1.88% 0.22 0.22 1.88% add-zsh-hook
-----------------------------------------------------------------------------------
1) 1 11.65 11.65 98.12% 11.65 11.65 98.12% compinit
-----------------------------------------------------------------------------------
2) 1 0.22 0.22 1.88% 0.22 0.22 1.88% add-zsh-hook
zsh -i -c exit 0.03s user 0.03s system 45% cpu 0.142 total
効果がなかった対応
ファイル分割を止める
pathやエイリアスの設定ごとの分割していたファイルを .zshrc
のみに統一したが、効果はなかった。
ファイル分割している場合
% for i in $(seq 1 10); do time zsh -i -c exit > /dev/null ; done
zsh -i -c exit > /dev/null 0.17s user 0.04s system 89% cpu 0.245 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 87% cpu 0.240 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 86% cpu 0.246 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 88% cpu 0.241 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 95% cpu 0.221 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 86% cpu 0.246 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 88% cpu 0.244 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 89% cpu 0.243 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 92% cpu 0.232 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 88% cpu 0.238 total
.zshrc
に統一した場合
% for i in $(seq 1 10); do time zsh -i -c exit > /dev/null ; done
zsh -i -c exit > /dev/null 0.18s user 0.05s system 86% cpu 0.255 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 77% cpu 0.271 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 88% cpu 0.240 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 89% cpu 0.236 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 90% cpu 0.229 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 90% cpu 0.232 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 83% cpu 0.252 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 86% cpu 0.247 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 83% cpu 0.253 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 87% cpu 0.242 total
brew --prefixを呼び出さない
Naoto Ishizawa/zshの起動を高速化したを参考に brew --prefix
を呼び出さず直接パスを記述した。ある程度効果はあったが brew --prefix
の呼び出しがそもそも少なく、パスの直書きがあまり好ましくなかったので今回は採用しなかった。
brew --prefix
あり
% for i in $(seq 1 10); do time zsh -i -c exit > /dev/null ; done
zsh -i -c exit > /dev/null 0.18s user 0.05s system 96% cpu 0.235 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 97% cpu 0.208 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 97% cpu 0.208 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 96% cpu 0.210 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 97% cpu 0.209 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 95% cpu 0.216 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 97% cpu 0.211 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 97% cpu 0.208 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 97% cpu 0.209 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 96% cpu 0.210 total
brew --prefix
なし
% for i in $(seq 1 10); do time zsh -i -c exit > /dev/null ; done
zsh -i -c exit > /dev/null 0.17s user 0.04s system 97% cpu 0.217 total
zsh -i -c exit > /dev/null 0.16s user 0.04s system 96% cpu 0.209 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 96% cpu 0.210 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 94% cpu 0.217 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 96% cpu 0.212 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 97% cpu 0.209 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 95% cpu 0.214 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 98% cpu 0.210 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 98% cpu 0.208 total
zsh -i -c exit > /dev/null 0.17s user 0.04s system 100% cpu 0.204 total
zshメモ
組み込みモジュール
zshでは組み込みのモジュールを選択して読み込むことで、いくつかの機能を利用できる。(zshは $module_path
に配置されるバイナリをファイル名で検索する。)
% echo $module_path
/usr/lib/zsh/5.8.1
zshコマンドの引数
man zsh
にある以上の情報はない。
引数 | 説明 |
---|---|
-c | 第一引数に渡されたコマンドを実行する。 -c exit で正常終了することにより time コマンドで計測できる |
-i | 強制的にシェルをインタラクティブにする。 -c exit と併用することでシェルがファイルを読み込んだ後に正常終了させる |