最近在重构自己的配置,在速度优化方面主要参考的是 doom-emacs
。在浏览了其源码中相关的配置后,感觉受益匪浅,印象尤其深刻的是它的增量加载:诸如 org、magit之类的包一下子加载可能会有卡顿,因此可以在 emacs 空闲的时候先部分加载其依赖或组件,从而提高整体的流畅程度。
我重构自己配置的原因之一就是平时进入 org-mode 之类的主模式的时候免不了要卡顿一下,这下真的是刚想打瞌睡就有人送来枕头。我把这部分的代码从 doom 里抄了出来,和大家分享一下
(defvar elemacs-incremental-packages '(t)
"A list of packages to load incrementally after startup. Any large packages
here may cause noticeable pauses, so it's recommended you break them up into
sub-packages. For example, `org' is comprised of many packages, and can be
broken up into:
(elemacs-load-packages-incrementally
'(calendar find-func format-spec org-macs org-compat
org-faces org-entities org-list org-pcomplete org-src
org-footnote org-macro ob org org-clock org-agenda
org-capture))
This is already done by the lang/org module, however.
If you want to disable incremental loading altogether, either remove
`doom-load-packages-incrementally-h' from `emacs-startup-hook' or set
`doom-incremental-first-idle-timer' to nil. Incremental loading does not occur
in daemon sessions (they are loaded immediately at startup).")
(defvar elemacs-incremental-first-idle-timer 2.0
"How long (in idle seconds) until incremental loading starts.
Set this to nil to disable incremental loading.")
(defvar elemacs-incremental-idle-timer 0.75
"How long (in idle seconds) in between incrementally loading packages.")
(defvar elemacs-incremental-load-immediately (daemonp)
"If non-nil, load all incrementally deferred packages immediately at startup.")
(defun elemacs-load-packages-incrementally (packages &optional now)
"Registers PACKAGES to be loaded incrementally.
If NOW is non-nil, load PACKAGES incrementally, in `doom-incremental-idle-timer'
intervals."
(if (not now)
(setq elemacs-incremental-packages (append elemacs-incremental-packages packages ))
(while packages
(let* ((gc-cons-threshold most-positive-fixnum)
(req (pop packages)))
(unless (featurep req)
(message "Incrementally loading %s" req)
(condition-case-unless-debug e
(or (while-no-input
;; If `default-directory' is a directory that doesn't exist
;; or is unreadable, Emacs throws up file-missing errors, so
;; we set it to a directory we know exists and is readable.
(let ((default-directory user-emacs-directory)
(inhibit-message t)
file-name-handler-alist)
(require req nil t))
t)
(push req packages))
(error
(message "Failed to load %S package incrementally, because: %s"
req e)))
(if (not packages)
(message "Finished incremental loading")
(run-with-idle-timer elemacs-incremental-idle-timer
nil #'elemacs-load-packages-incrementally
packages t)
(setq packages nil)))))))
(defun elemacs-load-packages-incrementally-h ()
"Begin incrementally loading packages in `elemacs-incremental-packages'.
If this is a daemon session, load them all immediately instead."
(if elemacs-incremental-load-immediately
(mapc #'require (cdr elemacs-incremental-packages))
(when (numberp elemacs-incremental-first-idle-timer)
(run-with-idle-timer elemacs-incremental-first-idle-timer
nil #'elemacs-load-packages-incrementally
(cdr elemacs-incremental-packages) t))))
(add-hook 'emacs-startup-hook #'elemacs-load-packages-incrementally-h)
在我的电脑上测试,进入 org-mode 时的资源消耗可以减少 15%。
另外 doom 还给 use-package
加了 :defer-incrementally
关键字:
(defalias 'use-package-normalize/:defer-incrementally #'use-package-normalize-symlist)
(defun use-package-handler/:defer-incrementally (name _keyword targets rest state)
(use-package-concat
`((doom-load-packages-incrementally
',(if (equal targets '(t))
(list name)
(append targets (list name)))))
(use-package-process-keywords name rest state)))
但我不打算继续用 use-package
了,因此就没继续深入,感兴趣的可以自己去看看。