パッケージの読み込みタイミングに関するチューニング(遅延・非同期ロード、便利パッケージ依存脱却)を行う際、「本当にロードを遅延できているのか」「本当に依存パッケージを減らせているのか」あるいは「このパッケージを遅延することで起動はどれくらい速くなるのか」などを検証する道具があると便利です。
load-file-name
を利用した起動プロセスの解析
ファイルをロードするときには内部で load-file-name
変数がセットされます。これを利用して、この変数に対して add-variable-watcher
で監視を回すことで
- 各パッケージのロードタイミング
- ロードの依存関係 (誰がそのパッケージをロードしているのか)
- おおよその所要時間
などを解析することができます。
;; setup.el より
(defvar setup-tracker--level 0)
(defvar setup-tracker--parents nil)
(defvar setup-tracker--times nil)
(when load-file-name
(push load-file-name setup-tracker--parents)
(push (current-time) setup-tracker--times)
(setq setup-tracker--level (1+ setup-tracker--level)))
(add-variable-watcher
'load-file-name
(lambda (_ v &rest __)
(cond ((equal v (car setup-tracker--parents))
nil)
((equal v (cadr setup-tracker--parents))
(setq setup-tracker--level (1- setup-tracker--level))
(let* ((now (current-time))
(start (pop setup-tracker--times))
(elapsed (+ (* (- (nth 1 now) (nth 1 start)) 1000)
(/ (- (nth 2 now) (nth 2 start)) 1000))))
(with-current-buffer (get-buffer-create "*setup-tracker*")
(save-excursion
(goto-char (point-min))
(dotimes (_ setup-tracker--level) (insert "> "))
(insert
(file-name-nondirectory (pop setup-tracker--parents))
" (" (number-to-string elapsed) " msec)\n")))))
(t
(push v setup-tracker--parents)
(push (current-time) setup-tracker--times)
(setq setup-tracker--level (1+ setup-tracker--level))))))
たとえば私の Emacs でこれを使ってみると、 init.el
(init.elc
) がロードされるまでの様子が以下のように描画されます。
init.elc (27 msec)
> sublimity-attractive.elc (0 msec)
> sublimity-scroll.elc (0 msec)
> sublimity.elc (0 msec)
> hl-line.elc (0 msec)
> paren.elc (0 msec)
> elemental-theme.elc (0 msec)
> phi-autopair.elc (0 msec)
> saveplace.elc (0 msec)
> scratch-pop.elc (0 msec)
init.el
が直接ロードする Emacs Lisp ファイルは 9 個だけで、またどれも一瞬でロードが終わるものになっていることがわかります。間接的に他のパッケージを読み込むようなパッケージも含まれていません。これによって高速で起動します。
起動後は非同期ロードが動き始め、ロードに時間のかかるパッケージも少しづつ読まれていきます。こちらも様子を見てみると、たとえば rainbow-mode.elc
はこのプラグインだけで init.el
本体の 4 倍くらいロードに時間がかかっていることがわかります。これは絶対に遅延ないし非同期ロードした方がいいでしょう。
rainbow-mode.elc (122 msec)
> ansi-color.elc (6 msec)
> color.elc (1 msec)
> cl-lib.elc (32 msec)
> > cl-loaddefs.el (2 msec)
またよく見てみるとこのパッケージはさらに cl-lib
に依存していて、ロード時間 122 msec のうち約 1/4 の 32 msec は cl-lib
のロードに使われていることもわかります。これだけでも init.el
本体のロードにかかる時間と同じくらいです。 init.el
から cl-lib
依存を剥がしたい理由がわかってもらえると思います。
このように、実際のロードの様子や所要時間を確認しながらチューニングを進めていくことで、より確実にスピードアップを図ることができます。