Jul 11,
2016

Emacs startup time shaving

I wanted to streamline my Emacs configuration, so after a little of yak shaving, all the Emacs packages that I'm using are now configured to be lazy-loaded on demand. Basically I removed all the "require" instances, if we don't count the library that manages the packages.

This also took care of sensibly reducing the start-up time. I couldn't find my notes, but I think it went from around 2 seconds to probably 1.5 seconds, using emacs-init-time as reference.

I wasn't surprised to find available libraries to benchmark or trace Emacs' functions and ready made solutions to profile the start-up sequence like the The Emacs Startup Profiler (ESUP) or ProfileDotEmacs. So after using ProfileDotEmacs I've found out that, in my case, packages initialization is the most expensive operation with a combined 0.49 seconds or 64% of the total time, while my customization need 0.18 seconds to complete, 23% in percentage. This means that further tweaking the customization can give small gains.

Micromanagement

Recently I found a couple of suggestions to speed up the start-up sequence. One is to temporarily increase the activation threshold of the garbage collector, the other is to temporarily disable the handling of the magic file names when starting up.

The first one can be implemented like this:

;; temporarily increasing GC to speedup startup time
(setq gc-cons-threshold-default gc-cons-threshold)
(setq gc-cons-threshold (* 100 1024 1024))
(run-with-idle-timer
 2 nil
 (lambda ()
   ;; restore default GC value
   (setq gc-cons-threshold gc-cons-threshold-default)
   ;; clean up minibuffer
   (message nil)))

The value is increased from 800 KB to 100 MB and restored after a couple of seconds1. With this change I measured a reduction of around 0.3 seconds.

The second suggestion consists in wrapping the init.el file like this:

;; temporarily disable file name check to speedup startup time
(let ((file-name-handler-alist nil))
   ...
   the init file content
   ...
)

In my case this one removes about 0.1 seconds, the gain is quite modest but it doesn't clutter too much the init.el file so I decided to left it there.

In the end, according to emacs-init-time the start-up needs 1.1~1.2 seconds.

Dumping

After seeing that the biggest part of the start-up is spent enabling packages, I tried to include the package.el library in the dumped binary, but without success. Probably the reader is not aware how the Emacs executable is created, so in few words, during the build process a bare-bone Emacs instance called Temacs evaluate a file called loadup.el that loads the required elisp libraries and at the end the working environment is dumped in a file. This file is the executable that is run when Emacs is invoked.

After the little digression, package.el depends on cl-lib.el but this one fails on load with the error "Wrong type argument: stringp, 0". The problem is I'm not able to activate the Temacs' debugger, I'm surely doing something wrong because trying temacs -q doesn't drop me in the editor but insists always to evaluate the loadup.el file that triggers the error.

I'll look at it again another time, for the moment I'll stick with emacsclient.


  1. plenty of time to complete the start-up. ↩︎