Monday, August 21, 2017

Emacs Start-Up: Speeding It Up

Emacs Start-Up: Speeding It Up

1 TL;DR:

Describes my Emacs start-up file, and what I did to speed it up from
12 seconds to under 4 seconds.

2 Overview Of Steps

  • Byte-compile start-up files.
  • Temporarily increase gc-cons-threshold during startup.
  • Load package autoloads (not packages) during start-up.
  • Use eval-after-load to advantage for post-package setup.
  • Lexically bind file-name-handler-alist to nil if start-up
    is split across many files.
  • Used memoization to avoid network lookup of current location during startup.

I have a large number of elpa/melpa packages installed:

(length load-path)

With the above, my emacs (Emacs 26 built from Git) startup time is on
average 4 seconds. This includes starting up emacspeak (including
speech servers), as well as launching a number of project-specific
shell buffers. Given that I rarely restart Emacs, the startup time is
academic — but speeding up Emacs startup did get me to clean-up my
Emacs setup.

3 Introduction

I have now used Emacs for more than 25 years, and my Emacs start-up
has followed the same structure through this time.

  1. The init file defines a start-up-emacs function that does the
    bulk of the work.
  2. Package-specific configuration is split up into
    <package>-prepare.el files.
  3. All of these files are byte-compiled.

As a first step, I added code to my start-up file to time the loading
of various modules.

4 Load Byte-Compiled Start-Up File

I keep my emacs-startup.el checked into GitHub.
My Emacs init-file is a symlink to the byte-compiled version of the
above — this is something that goes back to my time as a
grad-student at Cornell (when GitHub of course did not exist).
That is also when I originally learnt the trick of temporarily setting
gc-cons-threshold to 8MB — Emacs' default is 800K.

5 Package Autoloads And eval-after-load

Over time, some of the package-specific setup files had come to
directly load packages — it just made it easier to do
package-specific setup at the time. As part of the cleanup, I updated
these to strictly load package-autoload files and wrapped post-package
setup code in eval-after-load — this is effectively the same as
using use-package.

6 Loading Files Faster

Emacs has an extremely flexible mechanism for loading files — this
means you can load compressed, encrypted or remote files without
having to worry about it. That flexibility comes at a cost — if you
are sure you dont need this flexibility during start-up, then locally
binding file-name-handler-alist to nil is a big win — in my
case, it sped things up by 50%.

7 Avoid Network Calls During Start-Up

In my case, I set calendar-latitude and calendar-longitude by
geocoding my address — geocoding is done by calling the Google Maps
API. The geocoding API is plenty fast that you normally dont notice
it — but it was adding anywhere from 1–3 seconds during
startup. Since my address doesn't change that often, I updated module
gmaps to use a memoized version. My address is set via Customize,
and the geocoded lat/long is saved to disk automatically.

8 References

  1. Emacs Speed What got it all started.
  2. file-name-handler-alist The article that gave me the most useful
    tip of them all.