Chapter 06

チューニングの効果を検証する

zk_phi
zk_phi
2020.09.24に更新

パッケージの読み込みタイミングに関するチューニング(遅延・非同期ロード、便利パッケージ依存脱却)を行う際、「本当にロードを遅延できているのか」「本当に依存パッケージを減らせているのか」あるいは「このパッケージを遅延することで起動はどれくらい速くなるのか」などを検証する道具があると便利です。

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 依存を剥がしたい理由がわかってもらえると思います。

このように、実際のロードの様子や所要時間を確認しながらチューニングを進めていくことで、より確実にスピードアップを図ることができます。