最近の CLI ツールには、.bashrc
や .zshrc
などで読み込むべきシェルスクリプトをツール自身が出力してくれるものがある。
利用者からすると設定ファイルをシンプルにできて、ツールの開発者からすると内容をあとから自由にコントロールでき、あと率直におしゃれだと感じる一方で、コマンドを実行することによるオーバーヘッドも気になるところ。ただ、だからと言ってコマンドの出力をベタ書きするというのもメンテナンス性が損なわれるのでやりたくない。
これに対して、すぐに思いつくのは出力されるスクリプトをキャッシュする作戦だろう。コマンドの実行結果を事前にファイルに書き出しておけば、シェルを起動するときはファイルを読み込むだけでよくなる。
ただ、キャッシュ機構を用意するのもそれなりに面倒である。それに、あまりキャッシュのためのコードが増えるようだと、ベタ書きに対する優位性が揺らいでくる。
そういうわけこれまで着手していなかったのだが、最近導入した chezmoi のテンプレート機能を使うことで、簡単に実現できることに気付いた。
テンプレート中で output
関数を使うと、コマンドを実行して出力された内容を埋め込んだファイルを配置できる。
テンプレートは chezmoi apply
のたびにレンダリングし直される。すなわち、コマンドも再実行されるため、ベタ書きしたときのようにメンテナンスが面倒になることもない。ただし、コマンドが冪等であるかは事前に確認しておくこと。
これだけで、chezmoi の宣言的な設定の枠組みの中で、コマンドの出力をキャッシュする機構を実現できた。しかし、効果が無ければそもそもやる必要がない。
ぶっちゃけ、冒頭で例に挙げた starship
や rtx
は、Rust で書かれているので起動が割と速かったりする (それでも1コマンドあたり数 ms くらい変わる)。一方で、brew shellenv
はシェルスクリプトで実装されているので、毎回立ち上げるとそこそこオーバーヘッドがある。
rtx activate zsh
と brew shellenv
の実行時間を比べてみると以下のような感じ。M1 MacBook Air のメモリ 16GB、GPU 8コアのモデルにて実行した。
実際に brew shellenv
を実行する場合と実行結果をキャッシュした場合を比較すると、以下のようになる。まっさらな環境で試すのが面倒で既存のコードのうち Homebrew 部分のみ書き換えての比較なので、何倍速いみたいな言い方はできないが、どれくらい差があるかはわかるはず。
これだけ差が出るのであれば、個人的にはやる価値はあったかなと思う。実際には、macOS と Linux に両対応するためのコードも入ってきて、それを静的に解決できるか動的に解決するかの違いも生じるので、さらに差は大きくなる (OS ごとの差分の吸収だけ chezmoi のテンプレートに寄せて brew shellenv
を呼び出す中間的な選択肢もあるが)。
今もシェルスクリプトでやっていた処理を Rust や Go のプログラムに委譲する流れだが、究極的にはそれらの呼び出し元のシェルスクリプトもすべてなくなって、全部 WebAssembly とかになったら面白そうだなと思った。そういうのすでにあるんだろうか。
なお、今回試している dotfiles 全体は以下で見られる。
https://github.com/mono0x/dotfiles/tree/0c1f0c5e8c121050bb7d7e4d41891f34b6629ef2
この状態での起動時間も参考までに載せておく。