Speeding Up Emacs
Table of Contents
1 Motivation
I've being using Emacs for quite some time now. After picking up several modes and
packages on the way, my init.el
grew linearly with the time spent. And so did
my Emacs' startup time. It clocked at around 5 seconds on my SSD equipped Mac and
crawled on the Mac at work, having rotating hard drive, taking whooping
9.1 seconds.
Figure 1: result of (emacs-init-time)
. Not a very pleasing way to start your day.
2 Attempts
I found several solutions to speed up Emacs start on the web. Since I'm fairly
satisfied with the performance once Emacs starts up, I'm interested only in
improving startup time. To benchmark that, I'm using inbuilt function emacs-init-time
emacs-init-time is an interactive autoloaded compiled Lisp function in `time.el'. (emacs-init-time) Return a string giving the duration of the Emacs initialization.
Since memory is cached once we load up an application, subsequent attempts to
benchmark loading time gives inaccurate results. To get around that, I'm using
purge
call to flush cached memory.
2.1 Garbage Collection Threshold (gc-cons-threshold
)
I found this reddit thread which claims to speedup by increasing the threshold of the bytes consumed before garbage collector is invoked.
gc-cons-threshold is a variable defined in `C source code'. Its value is 800000 Documentation: Number of bytes of consing between garbage collections. Garbage collection can happen automatically once this many bytes have been allocated since the last garbage collection. All data types count. Garbage collection happens automatically only when `eval' is called. By binding this temporarily to a large number, you can effectively prevent garbage collection during a part of the program. See also `gc-cons-percentage'.
I bumped up the value to 108, purged the memory and fired up Emacs and got 8.7 seconds. This was just few milliseconds less than original value (9.1 seconds). This is not very impressive, further I'm not sure how much of this improvement can be attributed to gc tweak and how much to cached memory (I'm not sure which all files are purged).
2.1.1 GC Caveats
As noted by RobThorpe on the same thread,
Be aware that the GC pause gets longer as you raise the cons-threshold.
If you raise it a lot it may become irritatingly long and you'll begin to notice it while editing.
Is it okay to play around with this value without knowing what it does for sure?
Is it possible that I'm loading far too many modules which is hitting the threshold
and consequently taking more time to clean up. I don't know, maybe I can visit
this solution again after implementing use-package
.
2.2 use-package
1
There are two main motivation for me to try use-package
:
- It cleans up
init.el
: it groups configuration for each mode into blocks. - It gives option to delay package's loading until it is needed.
After following use-package
documentation and Sacha Chua's dotemacs, I restructured
my init.el
. If you are new to use-package
, here's a quick crash course:
- Instead of 'requiring' package, you declare it as
(use-package foo)
. - You can toggle several switches which affects how the package is loaded:
:init
execute code before package is loaded.:config
execute code after package is loaded.:bind
bind keystrokes to function.:command
creates autoload for those commands.:ensure
installs package if not found on system. Great way to setup environment.:defer
defers loading of the package until needed. You can pass integer which loads the package after N seconds of idle time. Eg::defer 4
.- and many more. Read about them here README.
After refactoring my init.el
for couple of hours, I could defer loading of
most of the packages (with the exception being 'evil' mode and 'zenburn' theme).
My startup time reduced to 3.5 seconds and 2.1 seconds on Mac with SSD :D
Figure 2: (emacs-init-time)
after using use-package
module.
Here's a snippet from my init.el
:
(use-package evil :ensure t :demand :init (progn (setq evil-want-C-u-scroll t) ;; Change cursor color depending on mode (setq evil-emacs-state-cursor '("red" box) evil-normal-state-cursor '("green" box) evil-visual-state-cursor '("orange" box) evil-insert-state-cursor '("red" bar) evil-replace-state-cursor '("red" bar) evil-operator-state-cursor '("red" hollow)) (evil-mode 1))) (use-package magit :ensure t :defer 5) ;; highlight changes (use-package git-gutter-fringe :ensure t :diminish git-gutter-mode :config (global-git-gutter-mode))
2.3 TODO emacs --daemon
3 Conclusion
3.1 Macbook Pro (Late 2013 Model) SSD with 8GB Memory
Tweak | (emacs-init-time) (sec) |
---|---|
No Tweak | 4.8 |
gc-cons-threshold |
3.7 |
use-package |
2.1 |
3.2 Macbook Pro (Mid 2012 Model) HDD with 10GB Memory
Tweak | (emacs-init-time) (sec) |
---|---|
No Tweak | 9.1 |
gc-cons-threshold |
8.7 |
use-package |
6.7 |