巨大なプロジェクト以下でEmacsのファイルオープンが遅いので調査する
最近非常にコードベースの大きいプロジェクトを扱うことがあって、emacsでファイル遷移をすると異様に読み込みに時間がかかっていました。 init.elを見る限り、hookしている処理に重そうなものはなさそうですが、はて、何に時間が食われているのだろう?
ファイルを開くときの処理をdumpして、何に処理時間がかかっているのかをつきとめたい。 そこで、emacsのプロファイラを利用してどの関数が時間を食っているのか探ってみました。
こちらの記事にEmacsのプロファイリング機能が日本語でまとまっていて、参考になります。
今回は素直に profiler-start
を使ってcpu dumpをしてみます。
手順としては、
- まず
profiler-start
をしておく。cpuを選択 - ファイル遷移を実施
profiler-report
を実行する- 解析が終わったら
profiler-end
でプロファイラを止める
cpu利用時間で計測結果を出すとこんな感じになります。 どこの処理に時間がかかっているのか、パーセンテージで表示されるのでボトルネックを探しやすいです。
これを見ると、 projectile-update-mode-line
が時間を食っていそうです。
自分のモードラインでは projectile
の情報を表示に利用していないので、これはOFFにしてしまってもよさそうです。
(setq projectile-dynamic-mode-line nil)
こんな感じで、十分早くなるまで解析と、改善施策を入れていきます。いわゆる普通のパフォーマンスチューニングです。
最終的に私のボトルネックは projectile-project-root
に帰結しました。
このように、 projectile-project-root
が沢山呼ばれていることがわかります。ここではプロジェクトのルートディレクトリの探索をしているはず。
探索のための関数はinit.elから設定できますが、これまで特に困ってなかったので、私はデフォルトのままになっています。
docs.projectile.mx
公式ガイドによると、 projectile-root-top-down
は3番目なので、その前の関数では見つかっていないようです。
(defcustom projectile-project-root-functions '(projectile-root-local projectile-root-bottom-up projectile-root-top-down projectile-root-top-down-recurring) "A list of functions for finding project roots." :group 'projectile :type '(repeat function))
1番目の projectile-root-local
については、以下のように説明されています。
- projectile-root-local: looks for project path set via the buffer-local variable projectile-project-root. Typically you’d set this variable via .dir-locals.el and it will take precedence over everything else.
つまり、 .dir-locals.dl
にプロジェクトルートを示しておけば、探索自体しなくてよさそうな気がします。
((nil . ((projectile-project-root . "/mnt/c/path/to/project/root"))) (c++-mode . ((indent-tabs-mode . t))))
びっくりするくらい早くなりました! あとは .dir-locals.el
をVCSで管理するなり、ignoreするなりしておけば万事解決です。
まとめ
今回はEmacsに付属しているプロファイラを利用して、普段使いで気になった重い動作を調査して、チューニングしてみました。 ちょっと気になる動作があるときに、簡単に実施できて便利だと思いました。他にもトレーサーなどもあるので、場合によって色々試せそうです。
それではよいEmacsライフを!