Emacsの起動時間を短縮する方法 #Emacs #AdventCalendar

Emacs Advent Calendar 9日目です。

すみません書くのがものすごく遅れてしまいました...。なんとか年内に書けた...。

前の日は、color-themeでEmacsの見た目をきれいにする - Qiita

次の日は、emacsの設定ファイルをどうするかというのはおそらく共通の悩み - Qiita
です。

はじめに

Emacsをずっと使い続けていると様々な言語の設定や、Emacsの挙動を少し変える設定を付け加えたりと、設定がどんどん増えていきます。多く書きすぎると遅くなるのでかなりストレスフルになってしまいます。これはautoloadや、eval-after-loadを使えばかなり軽減することができます。

しかし、autoloadとeval-after-loadだと、慣れないとどの関数を設定すればいいか全くわからない状況に陥ってしまいがちです。さらに設定ファイルも煩雑になりがちです。

これを解決できるマクロで深町さんが紹介してるlazyloadというものがあります。
http://e-arrows.sakura.ne.jp/2010/03/macros-in-emacs-el.html

このマクロはautoloadとeval-after-loadが隠蔽されているのでだいぶ書きやすいという利点があります。この記事ではそのlazyloadを使った起動時間短縮のための設定を紹介します。

設定のやり方

使うマクロはこれです。

;; (lazyload (triger-function ...) "filename" &rest body)
(defmacro lazyload (func lib &rest body)
  `(when (locate-library ,lib)
     ,@(mapcar (lambda (f) `(autoload ',f ,lib nil t)) func)
     (eval-after-load ,lib
       '(progn
          ,@body)) t))

使い方はorg-modeを例にして説明します。

設定の仕方は、

  1. auto-mode-alistに拡張子と、トリガーとなるキーバインドの設定
  2. lazyloadに関数定義やモード中のキーバインドの設定
  3. フックの設定

の3つの設定をやっていきます。

1つ目は、auto-mode-alistに拡張子の登録と、トリガーとなる関数のキーバインドを設定しておきます。メジャーモードなどの場合登録しておかないと「◯◯.拡張子」のファイルを開いても意図したモードになってくれません。

トリガーとなる関数のキーバインドを設定しておくと、関数を読み込んだ時に設定が読み込まれるようになります。

マイナーモードや追加機能の場合はキーバインドの設定のみで大丈夫だと思います。

共通のパスの設定もlazyloadの外でやっておくといいです。

;; Dropboxのパス変数
(defvar dropbox-directory
  (cond
   ((equal system-type 'windows-nt) (concat "c:/Users/" user-login-name "/Dropbox"))
   (t "~/Dropbox")))

;; 拡張子の登録
(add-to-list 'auto-mode-alist '(".org$" . org-mode))

;; キーバインドの設定
(global-set-key "\C-cl" 'org-store-link)
;; (global-set-key "\C-cc" 'org-capture)
(global-set-key "\C-ca" 'org-agenda)
(global-set-key "\C-cb" 'org-iswitchb)

;; C-c r でorg-remember起動
(define-key global-map (kbd "C-c r") 'org-remember)


次にlazyloadの設定をします。ここでは(function ...)部分にトリガーとなるメジャーモードの関数や、キーバインドで設定した関数を入れていきます。こうすると◯◯.orgのファイルが読み込まれた時や、最初に設定したキーバインドが読み込まれた時に設定が読み込まれて起動時間を短縮できます。

filename部分は基本的に各メジャーモード、マイナーモード、追加機能のファイル名(拡張子なし)を入れておけばよいですが、org-modeの場合はorg-installが起点となるファイルになります。

body部分はフックを除いた設定、requireなどをどんどん入れていけば良いです。

;; lazyloadの設定
;; (lazyload (function ...) "filename" &rest body)
(lazyload (org-mode org-install org-store-link org-agenda org-iswitchb org-remember)
           "org-install"
    (require 'org-install)

    (defvar org-foo)
    (defun org-bar () ...)

    (define-key org-mode-map "\M-n" 'org-next-visible-link)
    (define-key org-mode-map "\M-p" 'org-previous-visible-link)
    ...
    )


最後に、lazyloadの後ろにフックの設定をしておきます。org-modeには特に設定するものがなかったのでfoo-functionというのを例にしています。lazyloadの中にhookを設定しておくとうまく動かないので、外で設定してください。

;; フックの設定
(add-hook 'org-mode-hook 'foo-function)

これで設定は終了です。

完成した設定例が下のコードです。

設定例
;;;;;;;;;;;;;;;;;;;
;; 設定例 org-mode

;; 共通のパス変数はlazyloadの外で宣言しておく
;; Dropboxのパス変数
(defvar dropbox-directory
  (cond
   ((equal system-type 'windows-nt) (concat "c:/Users/" user-login-name "/Dropbox"))
   (t "~/Dropbox")))

;; 拡張子の登録
(add-to-list 'auto-mode-alist '(".org$" . org-mode))

;; キーバインドの設定
(global-set-key "\C-cl" 'org-store-link)
;; (global-set-key "\C-cc" 'org-capture)
(global-set-key "\C-ca" 'org-agenda)
(global-set-key "\C-cb" 'org-iswitchb)

;; C-c r でorg-remember起動
(define-key global-map (kbd "C-c r") 'org-remember)

;; lazyloadの設定
;; (lazyload (function ...) "filename" ...)
(lazyload (org-mode org-install org-store-link org-agenda org-iswitchb org-remember)
           "org-install"
    (require 'org-install)

    (if (boundp 'dropbox-directory)
        (setq org-directory (concat dropbox-directory "/Documents/org/")))
    (setq org-default-notes-file (concat org-directory "agenda.org"))
    (setq org-mobile-directory (concat dropbox-directory "/MobileOrg"))
    (setq org-mobile-inbox-for-pull (concat dropbox-directory "/flagged.org"))
    (setq org-agenda-files
          (mapcar #'(lambda (x) (concat org-directory x))
                  '("work.org" "school.org" "home.org")))

    (defvar org-foo)
    (defun org-bar () ...)

    (define-key org-mode-map "\M-n" 'org-next-visible-link)
    (define-key org-mode-map "\M-p" 'org-previous-visible-link)
    ...
    )

;; フックの設定
(add-hook 'org-mode-hook 'foo-function)

lazyloadを使った結果

Windowsでlazyloadを使った時の結果を表示します。このときの設定は確か

だったと思います。(すみません来月補足します。)

下の結果はinit-loader.elを使った起動時間のログになります。lazyloadを使用した結果、ものによっては1秒近くかかっていた設定が0.001秒まで減るのでかなりの起動時間の短縮になります。頻繁に使うモードでもlazyloadをしておけば起動時間をかなり短縮することができます。

org-mode 0.921318
org-mode 0.005438999999999999

howm 0.26786200000000004
howm 0.002473

sequential-command 0.735374
sequential-command 0.0016799999999999999

e2wm 0.136477
e2wm 0.001269

ruby-mode.elc. 0.01617600000000008
ruby-mode.elc. 0.021811999999999998

この結果でruby-modeのみ若干時間がかかっていますが、これはすでにeval-after-loadなどで囲まれていたものをlazyloadに変更したので時間がかかっています。しかし、見た目がかなりすっきりして設定もしやすいのでlazyloadを使ったほうが楽です。

終わりに

これを使えばそこまで設定を削らずに設定を残すことができ、比較的楽にautoload,eval-after-loadの設定ができるので、使うと便利です。起動時間が長くて困っている人で、「autoloadとかわからない」という人はlazyloadを使ってみましょう。