2023/02/05追記:rtxを使ってstarship+asdfのプロンプトの表示が遅くなる問題を解決する
タイトルの通り。 promptのカスタマイズにはstarshipを使っていて*1、バージョン管理にはasdfを使うようにここ最近変更した。 それぞれ特に不満なく使っていたのだが、あるときからリポジトリ内でのプロンプトの表示が遅いというのに気がついてしまった。 starshipがWARNINGを吐いていたこともあり、被疑はstarshipにあるとわかっていた。 ↓WARNING例
[WARN] - (starship::utils): Executing command "ruby" timed out. [WARN] - (starship::utils): You can set command_timeout in your config to a higher value to allow longer-running commands to keep executing.
starshipにはexplainというサブコマンドがありこれで各モジュールの実行速度がわかるのでこれを実行した。
❯ starship explain Here's a breakdown of your prompt: hogehoge (1ms) - The current working directory on branch (<1ms) - The active branch of the repo in your current directory via 💎 v3.0.2 (128ms) - The currently installed version of Ruby via ⍱ (<1ms) - The currently installed version of Vagrant ❯ (<1ms) - A character (usually an arrow) beside where the text is entered in your terminal
この結果を見るとrubyの実行に時間がかかっているのがわかる。
バージョンを出すだけなのになぜこんな時間がかかるのか調べたところ、asdfのラッパーに原因があることがわかった。
というのもrubyコマンドを実行すると呼び出されるのが ~/.asdf/shims/ruby
なのだがこれはシェルスクリプトになっていて、内部的には ~/.asdf/bin/asdf
を呼び出している。
この ~/.asdf/bin/asdf
もシェルスクリプトになっているので、rubyを呼び出すまでにbashが2回呼び出されることになる。
これが遅くなっている原因ということがわかった。
解決策: asdf-direnvを使う
解決策がないか色々検索していたらasdfのリポジトリに同様の問題と思われるissueが起票されていた。 このissueはクローズされいないものの、issueコメント中でasdf-direnvが紹介されており試したところ解決した。
READMEがしっかりしているので見ればわかるのだが、一応インストール手順を書いておくと
1 asdf-direnvをインストールする。
asdf plugin-add direnv asdf install direnv latest asdf global direnv latest
2 シェル起動時フックを設定する。READMEではbashになっているが、私はfish-shellなのでそれに合わせて変更。
asdf exec direnv hook fish | source
3 direnvrcに設定をおこない use asdf
を利用できるようにする*2。
❯ cat ~/.config/direnv/direnvrc source "$(asdf direnv hook asdf)"
4 プロンプトが遅くなるディレクトリに移動し .envrc
に use asdf
を設定する。
❯ cd /path/to/hoge ❯ cat .envrc use asdf
5 direnv
を有効化する。有効化するとenvファイルのキャッシュが作られて、そのキャッシュを読み込むようだ。
❯ direnv allow direnv: loading /path/to/hoge/.envrc direnv: using asdf direnv: Creating env file ~/.asdf/installs/direnv/2.28.0/env/3677896208-2306459982-2809058868-537363346 direnv: loading ~/.asdf/installs/direnv/2.28.0/env/3677896208-2306459982-2809058868-537363346 direnv: using asdf direnv 2.28.0 direnv: using asdf perl 5.34.0 direnv: using asdf delta 0.7.1 direnv: using asdf ghq 1.1.7 direnv: using asdf starship 0.56.0 direnv: using asdf terraform 1.0.2 direnv: using asdf ruby 3.0.2 direnv: using asdf python 3.9.5 direnv: export +RUBYLIB ~PATH
作成されるenvファイルのキャッシュをみるとわかるが、asdfがインストールした各ツールの実体 (~/.asdf/installs/<tool-name>/$PATH
に設定しているようだった。
前述のbashスクリプトを呼び出さない分速くなるという寸法のようだ。
効果とデメリット
では実際どのくらい速くなるのか?
READMEにベンチマークの項目がありそこでは node --version
が 190ms -> 4ms になっており98%改善していると書いてある。
私の環境ではどうなったか?設定後の starship explain
をとってみた。
❯ starship explain Here's a breakdown of your prompt: hogehoge (1ms) - The current working directory on branch (2ms) - The active branch of the repo in your current directory via 💎 v3.0.2 (18ms) - The currently installed version of Ruby via ⍱ (<1ms) - The currently installed version of Vagrant ❯ (<1ms) - A character (usually an arrow) beside where the text is entered in your terminal
設定前の結果と比べて 128ms -> 18msになっている! 試行回数が少ないのでばらつきはあるだろうが気にならないレベルでプロンプトの表示が速くなったので満足。
しかしながら、デメリットがありこの高速化の恩恵を受けるには各ディレクトリで .envrc
の設定と direnv allow
の実行が必要である。
まぁ、シンプルなコマンドなのでワンライナーを作ってもいいかもしれない。
まとめ
asdf-direnv
を使うことでプロンプトの表示が遅くなる問題を解決することができてよかった。
少し手間が増えたものの恩恵は大きいのでこれからも使っていこうと思う。
今回デバッグするにあたり startship explain
というコマンドを知ったのだが、これは最近できたコマンドなのかな?
とても便利だったので今後何かあったら使っていきたい。