Refactor out map.el usage

After some profiling, it turns out map-put and map-delete are 5-7x
slower (more on Emacs 25) than delq, setf/alist-get and add-to-list for
small lists (under 250 items), which is exactly how I've been using
them.

The only caveat is alist-get's signature is different on Emacs 25, thus
a polyfill is necessary in core-lib.
This commit is contained in:
Henrik Lissner 2018-06-23 16:48:58 +02:00
parent f602a1f607
commit f6dc6ac74e
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
29 changed files with 177 additions and 146 deletions

View file

@ -12,7 +12,7 @@
;; Like persistent-soft, caches assume a 2-tier structure, where all caches are
;; namespaced by location.
(defvar doom-cache-alists ()
(defvar doom-cache-alists '(t)
"An alist of alists, containing lists of variables for the doom cache library
to persist across Emacs sessions.")
@ -24,7 +24,7 @@ name under `pcache-directory' (by default a subdirectory under
(defun doom|save-persistent-cache ()
"Hook to run when an Emacs session is killed. Saves all persisted variables
listed in `doom-cache-alists' to files."
(dolist (alist doom-cache-alists)
(dolist (alist (butlast doom-cache-alists 1))
(cl-loop with key = (car alist)
for var in (cdr alist)
if (symbol-value var)
@ -54,8 +54,8 @@ Warning: this is incompatible with buffer-local variables."
(dolist (var variables)
(when (doom-cache-exists var location)
(set var (doom-cache-get var location))))
(map-put doom-cache-alists location
(append variables (cdr (assq location doom-cache-alists)))))
(setf (alist-get location doom-cache-alists)
(append variables (cdr (assq location doom-cache-alists)))))
;;;###autoload
(defun doom-cache-desist (location &optional variables)
@ -63,10 +63,11 @@ Warning: this is incompatible with buffer-local variables."
`doom-cache-alists', thus preventing them from being saved between sessions.
Does not affect the actual variables themselves or their values."
(if variables
(map-put doom-cache-alists location
(cl-set-difference (cdr (assq location doom-cache-alists))
variables))
(map-delete doom-cache-alists location)))
(setf (alist-get location doom-cache-alists)
(cl-set-difference (cdr (assq location doom-cache-alists))
variables))
(delq (assq location doom-cache-alists)
doom-cache-alists)))
;;;###autoload
(defun doom-cache-get (key &optional location)

View file

@ -345,7 +345,7 @@ example; the package name can be omitted)."
(package-install name))
(if (not (package-installed-p name))
(doom--delete-package-files name)
(map-put doom-packages name plist)
(setf (alist-get name alist) plist)
name)))
;;;###autoload
@ -388,9 +388,10 @@ package.el as appropriate."
(unless (package-installed-p name)
(user-error "%s isn't installed" name))
(let ((inhibit-message (not doom-debug-mode))
(spec (assq name quelpa-cache))
quelpa-p)
(when (assq name quelpa-cache)
(setq quelpa-cache (map-delete quelpa-cache name))
(when spec
(setq quelpa-cache (delq spec quelpa-cache))
(quelpa-save-cache)
(setq quelpa-p t))
(package-delete (cadr (assq name package-alist)) force-p)
@ -439,11 +440,11 @@ calls."
"Update `quelpa-cache' upon a successful `package-delete'."
(doom-initialize-packages)
(let ((name (package-desc-name desc)))
(when (and (not (package-installed-p name))
(assq name quelpa-cache))
(setq quelpa-cache (map-delete quelpa-cache name))
(quelpa-save-cache)
(doom--delete-package-files name))))
(unless (package-installed-p name)
(when-let* ((spec (assq name quelpa-cache)))
(setq quelpa-cache (delq spec quelpa-cache))
(quelpa-save-cache)
(doom--delete-package-files name)))))
;;

View file

@ -78,10 +78,10 @@ BODY will be run when this dispatcher is called."
(append
(when aliases
`((dolist (alias ',aliases)
(map-put doom--dispatch-alias-alist alias ',cmd))))
`((map-put doom--dispatch-command-alist ',cmd
(list :desc ,docstring
:body (lambda (args) ,form))))))))
(setf (alist-get alias doom--dispatch-alias-alist) ',cmd))))
`((setf (alist-get ',cmd doom--dispatch-command-alist)
(list :desc ,docstring
:body (lambda (args) ,form))))))))
;;

View file

@ -3,7 +3,6 @@
;; Built in packages we use a lot of
(require 'subr-x)
(require 'cl-lib)
(require 'map)
(eval-and-compile
(unless EMACS26+
@ -11,7 +10,23 @@
;; if-let and when-let are deprecated in Emacs 26+ in favor of their
;; if-let* variants, so we alias them for 25 users.
(defalias 'if-let* #'if-let)
(defalias 'when-let* #'when-let))))
(defalias 'when-let* #'when-let)
;; `alist-get' doesn't have its 5th argument before Emacs 26
(defun doom*alist-get (key alist &optional default remove testfn)
"Return the value associated with KEY in ALIST.
If KEY is not found in ALIST, return DEFAULT.
Use TESTFN to lookup in the alist if non-nil. Otherwise, use `assq'.
This is a generalized variable suitable for use with `setf'.
When using it to set a value, optional argument REMOVE non-nil
means to remove KEY from ALIST if the new value is `eql' to DEFAULT."
(ignore remove) ;;Silence byte-compiler.
(let ((x (if (not testfn)
(assq key alist)
(assoc key alist testfn))))
(if x (cdr x) default)))
(advice-add #'alist-get :override #'doom*alist-get))))
;;
@ -402,7 +417,7 @@ The available conditions are:
collect `(add-hook ',hook #',hook-name))
`((add-hook 'after-change-major-mode-hook #',hook-name))))))
(match
`(map-put doom-auto-minor-mode-alist ,match ',mode))
`(add-to-list 'doom-auto-minor-mode-alist '(,match . ,mode)))
((user-error "Invalid `associate!' rules for mode [%s] (:modes %s :match %s :files %s :when %s)"
mode modes match files when)))))

View file

@ -35,8 +35,6 @@ A warning will be put out if these deprecated modules are used.")
session of Dooming. Will noop if used more than once, unless FORCE-P is
non-nil."
(when (or force-p (not doom-init-modules-p))
;; Set `doom-init-modules-p' early, so `doom-pre-init-hook' won't infinitely
;; recurse by accident if any of them need `doom-initialize-modules'.
(setq doom-init-modules-p t)
(when doom-private-dir
(condition-case e
@ -184,7 +182,7 @@ non-nil, return paths of possible modules, activated or otherwise."
;;
;; This will load X on the first invokation of `find-file-hook' (then it will
;; remove itself from the hook/function).
(defvar doom--deferred-packages-alist ())
(defvar doom--deferred-packages-alist '(t))
(after! use-package-core
(add-to-list 'use-package-deferring-keywords :after-call nil #'eq)
(setq use-package-keywords
@ -208,15 +206,19 @@ non-nil, return paths of possible modules, activated or otherwise."
(if (functionp hook)
(advice-remove hook #',fn)
(remove-hook hook #',fn)))
(map-delete doom--deferred-packages-alist ',name)
(delq (assq ',name doom--deferred-packages-alist)
doom--deferred-packages-alist)
(fmakunbound ',fn))))
(cl-loop for hook in hooks
collect (if (functionp hook)
`(advice-add #',hook :before #',fn)
`(add-hook ',hook #',fn)))
`((map-put doom--deferred-packages-alist
',name
'(,@hooks ,@(cdr (assq name doom--deferred-packages-alist)))))
(let (forms)
(dolist (hook hooks forms)
(push (if (functionp hook)
`(advice-add #',hook :before #',fn)
`(add-hook ',hook #',fn))
forms)))
`((unless (assq ',name doom--deferred-packages-alist)
(push '(,name) doom--deferred-packages-alist))
(nconc (assq ',name doom--deferred-packages-alist)
'(,@hooks)))
(use-package-process-keywords name rest state))))))

View file

@ -222,8 +222,8 @@ elsewhere."
doom-private-dir)
(setq plist (plist-put plist :private t)))
`(progn
,(if pkg-pin `(map-put package-pinned-packages ',name ,pkg-pin))
(map-put doom-packages ',name ',plist)
,(if pkg-pin `(setf (alist-get ',name package-pinned-packages) ,pkg-pin))
(setf (alist-get ',name doom-packages) ',plist)
(not (memq ',name doom-disabled-packages)))))
(defmacro packages! (&rest packages)

View file

@ -67,7 +67,9 @@ Also see `doom-before-switch-buffer-hook'.")
enable-recursive-minibuffers nil
frame-inhibit-implied-resize t
;; remove continuation arrow on right fringe
fringe-indicator-alist (map-delete fringe-indicator-alist 'continuation)
fringe-indicator-alist
(delq (assq 'continuation fringe-indicator-alist)
fringe-indicator-alist)
highlight-nonselected-windows nil
image-animate-loop t
indicate-buffer-boundaries nil
@ -126,13 +128,13 @@ Also see `doom-before-switch-buffer-hook'.")
(format "%s modeline segment" name))))
(cond ((and (symbolp (car body))
(not (cdr body)))
(map-put doom--modeline-var-alist name (car body))
`(map-put doom--modeline-var-alist ',name ',(car body)))
(add-to-list 'doom--modeline-var-alist (cons name (car body)))
`(add-to-list 'doom--modeline-var-alist (cons ',name ',(car body))))
(t
(map-put doom--modeline-fn-alist name sym)
(add-to-list 'doom--modeline-fn-alist (cons name sym))
`(progn
(fset ',sym (lambda () ,docstring ,@body))
(map-put doom--modeline-fn-alist ',name ',sym)
(add-to-list 'doom--modeline-fn-alist (cons ',name ',sym))
,(unless (bound-and-true-p byte-compile-current-file)
`(let (byte-compile-warnings)
(byte-compile #',sym))))))))
@ -549,7 +551,7 @@ frame's window-system, the theme will be reloaded.")
(custom-set-faces
(when (fontp doom-font)
(let ((xlfd (font-xlfd-name doom-font)))
(map-put default-frame-alist 'font xlfd)
(add-to-list 'default-frame-alist (cons 'font xlfd))
`(fixed-pitch ((t (:font ,xlfd))))))
(when (fontp doom-variable-pitch-font)
`(variable-pitch ((t (:font ,(font-xlfd-name doom-variable-pitch-font))))))
@ -712,7 +714,7 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original
(defun doom|init-ui ()
"Initialize Doom's user interface by applying all its advice and hooks."
;; Make `next-buffer', `other-buffer', etc. ignore unreal buffers.
(map-put default-frame-alist 'buffer-predicate #'doom-buffer-frame-predicate)
(add-to-list 'default-frame-alist (cons 'buffer-predicate #'doom-buffer-frame-predicate))
;; Switch to `doom-fallback-buffer' if on last real buffer
(advice-add #'kill-this-buffer :around #'doom*switch-to-fallback-buffer-maybe)
;; Don't kill the fallback buffer