Table of Contents
這兩天一直在折騰 Emacs 的配置文件以優化啓動時間,這裏作個簡單的總結。 css
1 啓動(加載)時間檢測
檢測配置文件的加載時間很是簡單,開始加載之間加個時間戳,加載結束以後計算一下時間便可: html
;; Load all configuration and packages. (let ((ts-init (current-time))) (setq missing-packages-list nil package-init-statistic nil) (try-require '01-rc-generic t) (try-require '02-rc-functions t) (try-require '04-rc-other-modes t) (try-require '05-rc-misc t) (try-require '03-rc-prog-mode t) (try-require '06-rc-complete t) (try-require '09-rc-keybindings t) (try-require '10-emacs-custome t) (try-require '99-proj t) (try-require '100-private t) ;; Report package statistics. (message "\n\nShowing package initialization statistics:\n%s" (mapconcat (lambda (x) (format "package %s cost %.2f seconds" (car x) (cdr x))) (reverse package-init-statistic) "\n" )) (message "Finished startup in %.2f seconds, %d packages missing%s\n\n" (float-time (time-since ts-init)) (length missing-packages-list) (if missing-packages-list ". Refer to `missing-packages-list` for missing packages." ".")))
上面代碼中有個 try-require ,最先取自 這裏 , 後來對他加了一些改動: java
;; Function to collect information of packages. (defvar missing-packages-list nil "List of packages that `try-require' can't find.") (defvar package-init-statistic nil "Package loading statistics") ;; attempt to load a feature/library, failing silently (defun try-require (feature &optional click) "Attempt to load a library or module. Return true if the library given as argument is successfully loaded. If not, instead of an error, just add the package to a list of missing packages." (condition-case err ;; protected form (let ((timestamp (current-time)) (package (if (stringp feature) feature (symbol-name feature)))) (if (stringp feature) (load-library feature) (require feature)) (if click (add-to-list 'package-init-statistic (cons (if (stringp feature) feature (symbol-name feature)) (float-time (time-since timestamp))))) (message "Checking for library `%s'... Found, cost %.2f seconds" feature (float-time (time-since timestamp)))) ;; error handler (file-error ; condition (progn (message "Checking for library `%s'... Missing" feature) (add-to-list 'missing-packages-list feature 'append)) nil)))
該函數檢查 feature 時候存在,若是在則加載並輸出加載所用的時間,若是不在,輸出警告信息並記錄缺失的文件。 同時,若是可選參數 click 被設置成,則還將加載耗費的時間信息記錄到全局的 package-init-statistic 中,以便後續查詢。 git
在個人機器上,上述配置會產生下面的信息: sql
Showing package initialization statistics: package 01-rc-generic cost 0.16 seconds package 02-rc-functions cost 0.15 seconds package 04-rc-other-modes cost 0.03 seconds package 05-rc-misc cost 0.53 seconds package 03-rc-prog-mode cost 1.22 seconds package 06-rc-complete cost 0.18 seconds package 09-rc-keybindings cost 0.08 seconds package 10-emacs-custome cost 0.00 seconds package 99-proj cost 0.00 seconds package 100-private cost 0.01 seconds Finished startup in 2.40 seconds, 0 packages missing.
可見配置信息加載以後總共話費了 2.4 秒,相對於 Emacs 強大的功能來講,加載時間還算不錯,尤爲是啓動以後 server-mdoe 會自動啓動,此時再用 emacs-client 來打開文件時,時間消耗會很是小了。 express
2 autoload and eval-after-load
上述代碼中僅簡單說明了要經過 try-require 來加載哪些配置文件,單個配置文件中則大量使用了 autoload 和 eval-after-load 來將 mode 相關的配置時間儘可能延後,關於 autoload 和 eval-after-load ,這裏 有比較詳細的解釋,簡單來講, windows
- autoload 能夠從讓 Emacs 知道去哪一個文件中去找指定的函數或者定義,但不用去真正的加載它。
- eval-after-load 則能夠安排 Emacs 在真正加載某個文件或者符號的時候去作一些事情。
有時候我還想知道 eval-after-load 安排的事情在執行時候花費多少時間,爲此寫了個 Emacs Macro: bash
(defmacro yc/eval-after-load (name &rest args) "Macro to set expressions in `arg` to be executed after `name` is loaded." `(eval-after-load ,name ',(append (list 'progn `(let ((ts (current-time))) (message "Loading configuration for: %s..." ,name) ,@args (message "Configuration for %s finished in %.2f seconds" ,name (float-time (time-since ts ))))))))
使用實例: app
(autoload 'erlang-mode "erlang" "erlang" t) (add-to-list 'auto-mode-alist '("\\.erl\\'" . erlang-mode)) (add-to-list 'auto-mode-alist '("\\.escript\\'" . erlang-mode)) (yc/eval-after-load "erlang" (try-require 'erlang-flymake) (let ((erl-root (cond ((string= system-type "windows-nt") nil) ((string= system-type "darwin") nil) (t "/usr/lib/erlang")))) (setq erlang-root-dir erl-root)))
上面的代碼告訴 Emacs 從 "erlang.el" 這個文件中能夠找到 erlang-mode 這個 Mode, 並安排在加載 erlang.el 以後加載 erlang-flymake 並設置一些變量。 當打開一個 erl 文件後,會有以下的輸出: less
Loading configuration for: erlang... Checking for library `erlang-flymake'... Found, cost 0.00 seconds Configuration for erlang finished in 0.00 seconds
3 backtrace
backtrace 固然是用來打印 backtrace 的,它不會幫助解決文件加載速度,但若是咱們以爲某些文件不該該加載,但啓動時候卻自動加載了,那麼能夠在 eval-after-load 的後面加上這句話,而後看下 backtrace 。
我遇到的一個問題是,每次 Emacs 啓動時候都會自動嘗自動加載 magit 的配置,搜索無果後在 magit 的配置裏面加上 backtrace , 結果定位到是其餘的 package 裏用了 (require 'magit) ,對那個 package 稍做修改便可。
4 function and advice
這個 advice 其實和 Emacs 啓動加載沒什麼關係,但懶得單獨再寫什麼東西了,這裏簡單記上兩筆。
- Emacs 很獨特,用戶能夠爲某個 function 給出一些建議,並可讓該建議在函數執行以前,以後,或者以前加上以後來執行。
- 對於上面的第三種狀況 (around-advice),若是 advice 中沒有用到 ad-do-it ,則 function 自己無任何做用,至關於覆蓋了原有的function。
- advice 中能夠經過變量 ad-return-value 來設置返回值。
更具體的信息能夠查看 elisp 的 info ,這裏幾下一個簡單的例子, hideif 中的 hide-ifdefs 總會輸出信息來通知用戶 hiding 的啓動和結束,很討厭,咱們能夠經過 advice 來直接覆蓋掉原來的函數:
(autoload 'hide-ifdef-mode "hideif" "" t) (yc/eval-after-load "hideif" (setq hide-ifdef-shadow t) (setq-default hide-ifdef-env '((__KERNEL__ . 1) (DEBUG . 1) (CONFIG_PCI . 1) )) (defadvice hide-ifdefs (around yc/hideifdefs (&optional nomsg)) (interactive) (setq hif-outside-read-only buffer-read-only) (unless hide-ifdef-mode (hide-ifdef-mode 1)) ; turn on hide-ifdef-mode (if hide-ifdef-hiding (show-ifdefs)) ; Otherwise, deep confusion. (setq hide-ifdef-hiding t) (hide-ifdef-guts) (setq buffer-read-only (or hide-ifdef-read-only hif-outside-read-only)) ) (ad-activate 'hide-ifdefs) (defun yc/add-to-ifdef-env (lst) "Helper function to update ifdef-env." (let (kvp k v) (while (setq kvp (pop lst)) (setq k (car kvp) v (cdr kvp)) (hif-set-var k v) (when (and (symbolp k) (symbolp v)) (add-to-list 'semantic-lex-c-preprocessor-symbol-map (cons (symbol-name k) (symbol-name v))))))) (defun yc/toggle-hide-if-def-shadow () "Toggle shadow" (interactive) (setq hide-ifdef-shadow (not hide-ifdef-shadow)) (hide-ifdefs)) ;; Copied from Ahei: ;; http://code.google.com/p/dea/source/browse/trunk/my-lisps/hide-ifdef-settings.el (defun hif-goto-endif () "Goto #endif." (interactive) (unless (or (hif-looking-at-endif) (save-excursion) (hif-ifdef-to-endif)))) (defun hif-goto-if () "Goto #if." (interactive) (hif-endif-to-ifdef)) (defun hif-goto-else () "Goto #else." (interactive) (hif-find-next-relevant) (cond ((hif-looking-at-else) 'done) ((hif-ifdef-to-endif) ; find endif of nested if (hif-goto-endif)) ; find outer endif or else ((hif-looking-at-else) (hif-goto-endif)) ; find endif following else ((hif-looking-at-endif) 'done) (t (error "Mismatched #ifdef #endif pair")))) )