diff --git a/sources/tech/20171119 Advanced Techniques for Reducing Emacs Startup Time.md b/sources/tech/20171119 Advanced Techniques for Reducing Emacs Startup Time.md new file mode 100644 index 0000000000..6a761ac7d1 --- /dev/null +++ b/sources/tech/20171119 Advanced Techniques for Reducing Emacs Startup Time.md @@ -0,0 +1,252 @@ +[#]: collector: (lujun9972) +[#]: translator: (lujun9972) +[#]: reviewer: ( ) +[#]: publisher: ( ) +[#]: url: ( ) +[#]: subject: (Advanced Techniques for Reducing Emacs Startup Time) +[#]: via: (https://blog.d46.us/advanced-emacs-startup/) +[#]: author: (Joe Schafer https://blog.d46.us/) + +Advanced Techniques for Reducing Emacs Startup Time +====== + +Six techniques to reduce Emacs startup time by the author of the [Emacs Start Up Profiler][1]. + +tl;dr: Do these steps: + + 1. Profile with Esup. + 2. Adjust the garbage collection threshold. + 3. Autoload **everything** with use-package. + 4. Avoid helper functions which cause eager loads. + 5. See my Emacs [config][2] for an example. + + + +### From .emacs.d Bankruptcy to Now + +I recently declared my third .emacs.d bankruptcy and finished the fourth iteration of my Emacs configuration. The evolution was: + + 1. Copy and paste elisp snippets into `~/.emacs` and hope it works. + 2. Adopt a more structured approach with `el-get` to manage dependencies. + 3. Give up and outsource to Spacemacs. + 4. Get tired of Spacemacs intricacies and rewrite with `use-package`. + + + +This article is a collection of tips collected during the 3 rewrites and from creating the Emacs Start Up Profiler. Many thanks to the teams behind Spacemacs, use-package and general. Without these dedicated voluteers, this task would be vastly more difficult. + +### But What About Daemon Mode + +Before we get started, let me acknowledge the common retort when optimizing Emacs: “Emacs is meant to run as a daemon so you’ll only start it once.” That’s all well and good except: + + * Fast things feel nicer. + * When customizing Emacs, you sometimes get into weird states that can be hard to recover from without restarting. For example, if you add a slow `lambda` function to your `post-command-hook`, it’s tough to remove it. + * Restarting Emacs helps verify that customization will persist between sessions. + + + +### 1\. Establish the Current and Best Possible Start Up Time + +The first step is to measure the current start up time. The easy way is to display the information at startup which will show progress through the next steps. + +``` +(add-hook 'emacs-startup-hook + (lambda () + (message "Emacs ready in %s with %d garbage collections." + (format "%.2f seconds" + (float-time + (time-subtract after-init-time before-init-time))) + gcs-done))) +``` + +Second, measure the best possible startup speed so you know what’s possible. Mine is 0.3 seconds. + +``` +emacs -q --eval='(message "%s" (emacs-init-time))' + +;; For macOS users: +open -n /Applications/Emacs.app --args -q --eval='(message "%s" (emacs-init-time))' +``` + +### 2\. Profile Emacs Startup for Easy Wins + +The [Emacs StartUp Profiler][1] (ESUP) will give you detailed metrics for top-level expressions. + +![esup.png][3] + +Figure 1: + +Emacs Start Up Profiler Screenshot + +WARNING: Spacemacs users, ESUP currently chokes on the Spacemacs init.el file. Follow for updates. + +### 3\. Set the Garbage Collection Threshold Higher during Startup + +This saves about ****0.3 seconds**** on my configuration. + +The default value for Emacs is 760kB which is extremely conservative on a modern machine. The real trick is to lower it back to something reasonable after initialization. This saves about 0.3 seconds on my init files. + +``` +;; Make startup faster by reducing the frequency of garbage +;; collection. The default is 800 kilobytes. Measured in bytes. +(setq gc-cons-threshold (* 50 1000 1000)) + +;; The rest of the init file. + +;; Make gc pauses faster by decreasing the threshold. +(setq gc-cons-threshold (* 2 1000 1000)) +``` + +### 4\. Never require anything; autoload with use-package instead + +The best way to make Emacs faster is to do less. Running `require` eagerly loads the underlying source file. It’s rare the you’ll need functionality immediately at startup time. + +With [`use-package`][4], you declare which features you need from a package and `use-package` does the right thing. Here’s what it looks like: + +``` +(use-package evil-lisp-state ; the Melpa package name + + :defer t ; autoload this package + + :init ; Code to run immediately. + (setq evil-lisp-state-global nil) + + :config ; Code to run after the package is loaded. + (abn/define-leader-keys "k" evil-lisp-state-map)) +``` + +To see what packages Emacs currently has loaded, examine the `features` variable. For nice output see [lpkg explorer][5] or my variant in [abn-funcs-benchmark.el][6]. The output looks like: + +``` +479 features currently loaded + - abn-funcs-benchmark: /Users/jschaf/.dotfiles/emacs/funcs/abn-funcs-benchmark.el + - evil-surround: /Users/jschaf/.emacs.d/elpa/evil-surround-20170910.1952/evil-surround.elc + - misearch: /Applications/Emacs.app/Contents/Resources/lisp/misearch.elc + - multi-isearch: nil + - +``` + +### 5\. Avoid Helper Functions to Set Up Modes + +Often, Emacs packages will suggest running a helper function to set up keybindings. Here’s a few examples: + + * `(evil-escape-mode)` + * `(windmove-default-keybindings) ; Sets up keybindings.` + * `(yas-global-mode 1) ; Complex snippet setup.` + + + +Rewrite these with use-package to improve startup speed. These helper functions are really just sneaky ways to trick you into eagerly loading packages before you need them. + +As an example, here’s how to autoload `evil-escape-mode`. + +``` +;; The definition of evil-escape-mode. +(define-minor-mode evil-escape-mode + (if evil-escape-mode + (add-hook 'pre-command-hook 'evil-escape-pre-command-hook) + (remove-hook 'pre-command-hook 'evil-escape-pre-command-hook))) + +;; Before: +(evil-escape-mode) + +;; After: +(use-package evil-escape + :defer t + ;; Only needed for functions without an autoload comment (;;;###autoload). + :commands (evil-escape-pre-command-hook) + + ;; Adding to a hook won't load the function until we invoke it. + ;; With pre-command-hook, that means the first command we run will + ;; load evil-escape. + :init (add-hook 'pre-command-hook 'evil-escape-pre-command-hook)) +``` + +For a much trickier example, consider `org-babel`. The common recipe is: + +``` +(org-babel-do-load-languages + 'org-babel-load-languages + '((shell . t) + (emacs-lisp . nil))) +``` + +This is bad because `org-babel-do-load-languages` is defined in `org.el`, which is over 24k lines of code and takes about 0.2 seconds to load. After examining the source code, `org-babel-do-load-languages` is simply requiring the `ob-` package like so: + +``` +;; From org.el in the org-babel-do-load-languages function. +(require (intern (concat "ob-" lang))) +``` + +In the `ob-.el`, there’s only two methods we care about, `org-babel-execute:` and `org-babel-expand-body:`. We can autoload the org-babel functionality instead of `org-babel-do-load-languages` like so: + +``` +;; Avoid `org-babel-do-load-languages' since it does an eager require. +(use-package ob-python + :defer t + :ensure org-plus-contrib + :commands (org-babel-execute:python)) + +(use-package ob-shell + :defer t + :ensure org-plus-contrib + :commands + (org-babel-execute:sh + org-babel-expand-body:sh + + org-babel-execute:bash + org-babel-expand-body:bash)) +``` + +### 6\. Defer Packages you don’t need Immediately with Idle Timers + +This saves about ****0.4 seconds**** for the 9 packages I defer. + +Some packages are useful and you want them available soon, but are not essential for immediate editing. These modes include: + + * `recentf`: Saves recent files. + * `saveplace`: Saves point of visited files. + * `server`: Starts Emacs daemon. + * `autorevert`: Automatically reloads files that changed on disk. + * `paren`: Highlight matching parenthesis. + * `projectile`: Project management tools. + * `whitespace`: Highlight trailing whitespace. + + + +Instead of requiring these modes, ****load them after N seconds of idle time****. I use 1 second for the more important packages and 2 seconds for everything else. + +``` +(use-package recentf + ;; Loads after 1 second of idle time. + :defer 1) + +(use-package uniquify + ;; Less important than recentf. + :defer 2) +``` + +### Optimizations that aren’t Worth It + +Don’t bother byte-compiling your personal Emacs files. It saved about 0.05 seconds. Byte compiling causes difficult to debug errors when the source file gets out of sync with compiled file. + + +-------------------------------------------------------------------------------- + +via: https://blog.d46.us/advanced-emacs-startup/ + +作者:[Joe Schafer][a] +选题:[lujun9972][b] +译者:[译者ID](https://github.com/译者ID) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://blog.d46.us/ +[b]: https://github.com/lujun9972 +[1]: https://github.com/jschaf/esup +[2]: https://github.com/jschaf/dotfiles/blob/master/emacs/start.el +[3]: https://blog.d46.us/images/esup.png +[4]: https://github.com/jwiegley/use-package +[5]: https://gist.github.com/RockyRoad29/bd4ca6fdb41196a71662986f809e2b1c +[6]: https://github.com/jschaf/dotfiles/blob/master/emacs/funcs/abn-funcs-benchmark.el