はじめに

Emacs の起動を早くしたい. Windows 環境 (Cygwin) 環境における Emacs の起動時間が異常.

というわけで, 改善に向けた取り組みを実施した.

計測

起動時間の把握

M-x emacs-init-time という関数を実行すると, 起動にかかる時間がわかる.

とりあえず, 現状は

Machine Time


Arch Linux 12.1 Windows 29.6

esup をつかう

esup を利用すると, 詳細な起動時間がわかる.

(require 'esup)

自分の環境だと, inits ディレクトリ配下がすべて結果が要約されてしまい, よくわからなかった.

initchart をつかう

Emacs のスタートアップを視覚化する.

(require 'initchart)
(initchart-record-execution-time-of load file)
(initchart-record-execution-time-of require feature)

init log の確認

elisp の管理には init-loader.el を利用してる. init log を確認すると, 各処理における読み込み時間がわかる.

Windows における時間.

loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/00_el-get.elc. 0.0
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/01_global.el. 2.2812822
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/03_display.el. 1.0277368
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/20_text.elc. 4.8438096999999996
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/30_programming.elc. 1.8992336
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/31_c_cpp.elc. 0.1406281
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/31_haskell.elc. 0.2656291
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/31_java.elc. 0.3750235
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/31_python.elc. 1.9972729
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/31_ruby.elc. 0.3281107
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/33_minorlang.elc. 2.2702037
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/40_helm.elc. 1.1719071
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/50_org-mode.elc. 1.2500322
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/60_utility.el. 7.3733458
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/61_wanderlust.elc. 0.13878569999999998
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/90_color.elc. 0.7656375
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/99_windows8.elc. 0.171875
loaded c:/cygwin64/home/tsu-nera/dotfiles/.emacs.d/inits/windows-init.elc. 0.1406362

分析

  • utility が一番のボトルネック
    • 利用していないものはコメントアウト.
  • プログラミング言語に関する lisp は普段利用しないのに読み込みに時間かかかっている.
    • 遅延ロードをすべてに適用する
  • text 編集関係に時間がかかっている.
    • 利用しないものはコメントアウト

課題

当たり前の結果だけれども, 以下の 2 点に絞られるようだ.

  • 遅延評価の積極的適用
  • 不要な行の削除

カイゼン

emacsclient をつかう

これはつかう, というよりもすでにつかっている. というより, これをつかわないと話にならない.

ちなみに, 早く立ち上げたいときは, emacs -q オプションを指定することで設定ファイルを読み込まずに立ち上げる.

X Windows 環境での起動カイゼン

よくワカってないけど, コンナおまじないがあるらしい.

(modify-frame-parameters nil '((wait-for-wm . nil)))

バイトコンパイルをする

パッケージ管理には el-get を利用してる. そして, elisp の管理には init-loader を使っている.

el-get で取得したものは自動的にバイトコンパイルされるのだが, 盲点があった. それが

  1. el-get 自体
  2. init-loader 自体
  3. inits 配下の設定ファイル

1,2 については, el-get でいままで管理していなかった. これらを el-get 配下で管理するようにしていた.

さらに, inits 配下の設定ファイルについては 編集するたびにバイトコンパイルするようにした.

を参考にして, 以下を init.el に記述.

;;; 設定ファイルのあるフォルダを指定
(setq inits_dir (expand-file-name "~/.emacs.d/inits/"))
(init-loader-load inits_dir)

;; inits フォルダのみ, 保存時に自動コンパイルして即反映させる
;; https://fukuyama.co/emacsd
(defun auto-save-byte-compile-file ()
  "Do `byte-compile-file' and reload setting immediately, When elisp file saved only in inits folder."
  (interactive)
  (when (or (equal default-directory inits_dir)
        (equal default-directory (abbreviate-file-name inits_dir)))
    (byte-compile-file buffer-file-name t)
    ))
(add-hook 'emacs-lisp-mode-hook
      (lambda ()
        (add-hook 'after-save-hook 'auto-save-byte-compile-file
;; nil t)))

init-loader で自動バイトコンパイル機能があるようだ.

(setq init-loader-byte-compile t)

有効にしたが, イマイチ使い方がよくわからない.

起動時にバイトコンパイル

野良 elisp もバイトコンパイルしておいた方が早い.

init.el に以下を追記する. 末尾に 0 をつけることで, 起動時にバイトコンパイル.

(byte-recompile-directory "~/.emacs.d/myelisp" 0)
(byte-recompile-directory "~/.emacs.d/elisp" 0)

設定ファイルに直書きしている関数も外部ファイルに出してバイトコンパイル

これは結構効果的だった.

ステップ数の多い defun は別のファイルに分けた.さらに

  • 末尾に (provide ‘hogehoge) を追加
  • gist に登録
  • el-get で取得する recipe 作成

することで, gist で外部ファイルを管理することにした.

遅延ロードを適用する

autoload をつかう

ライブラリを起動時にロードする必要がない場合は, autoload を利用する. xxx-command を利用するときになって初めて"xxx"がロードされる.

(autoload 'xxx-command "xxx")

起動時にロードしないことで, 高速化.

eval-after-load/hook つかう

あるモードでのみ利用するものは, eval-after-load or hook を利用する.

;; hook
(add-hook 'c-mode-common-hook
      '(lambda ()
         (gtags-mode 1)
         (gtags-make-complete-list)))

;;eval-after-load
(eval-after-load "isearch"
  '(progn
     (require 'isearch-dabbrev)
     (define-key isearch-mode-map (kbd "<tab>") 'isearch-dabbrev-expand)))

2 つの違いは以下が詳しい.

要約すると,

  • 一度だけ設定すればよいものは eval-after-load
  • バッファを開くたびに設定したいもの hook

Emacs24.4 からは with-eval-after-load という関数がある. これも以下の記事が詳しい.

use-package をつかう

autoload/eval-after-load の記述を簡潔に書くための elisp.

記述方法は以下の記事がとても詳しい. ここでは省略.

起動時のエラーログや load ログを除去

起動時のログで怪しいものは極力調べて排除した.

結果

起動速度が半分になった! v (^^) v

Machine Time diff


Arch Linux 6.0 -6.1 Windows 16.7 12.9

リフリァクタリングした 設定ファイル