Table of Contents

1 The Emacs Garbage Collection Magic Hack - update

<2019-04-11 Thu>

The proposed original hack evolved into the GCMH.

Respect to the original hack GCMH has a slightly refined strategy.

During pauses garbage collection is reenabled using the default conventional strategy.

This makes sure that in case some elisp is consing (maybe triggered by a timer) when you restart using your Emacs will be as much garbage collected as possible.

GCMH is now also available on MELPA https://melpa.org/#/gcmh

PS: Some discussion on emacs-devel and reddit about GCMH.

PPS: Do not forget to try out customizing `gcmh-verbose' to t to get an idea about how much time I did saved you! ;)

2 The original magic Emacs garbage collection hack

<2019-03-11 Mon>

Living into the GNU Emacs Lisp Machine is as attractive as convenient.

One of the many advantages is being able to forget about the barbarian practice of the manual memory management.

Unfortunately everything comes with a price and Emacs as to recollect all unused memory from time to time, here we come to (in)famous garbage collector.

This post is not about the details of the Emacs GC but rather on how to tweak it to suit our needs.

If you have an Emacs running since a while just check the gc-elapsed variable. This is the time spent in GC for the current session in second. You'll get an idea of how much is unacceptable this problem…

We'll certainly have to be sneaky to fix it.

The first thing you want to do is probably to see when GC is running.

(setq garbage-collection-messages t)

Using Emacs for a while with this setup you'll notice that when Elisp is running and consing GC is coming into play really often.

This is infact the reason why we do not notice it, it's just running all the time…

In the moment that garbage collection becomes visible to you the next immediate and obvious consequence step is that you are starving to optimize it.

The first information you'll find about how to trim the Emacs GC is the gc-cons-threshold variable. This represents (as the manual says) the "Number of bytes of consing between garbage collections."

The default value (800KB) is quite small but this should not come as a surprise seen the fact that Emacs was made to run on really constrained hardware systems.

The first optimization attempt would be to increase this threshold. This is done by many (say mosts) but unfortunately does not solve the problem but just make it worst.

An higher threshold certainly reduce the number of GC runs and with that the total GC time (due to the fixed cost of every single run) but produce longer runs translating in more annoying hangs of the whole universe.

Infact what you want to do is to have the GC happening when you are not using Emacs actively; Say when you are thinking while reading, annoying a colleague or having a coffee.

The GC is not smart enough to understand when you are going to use the keyboard to input a command in the nearhood future, nobody can predict the future infact (not even Emacs) but here the interval distribution comes into play to help us. The long story short is that is possible to extrapolate from interval distribution that if something did not happened for a while is unlikely to happen soon. Surprise!!

With this truth revealed this is the trick I propose:

(defmacro k-time (&rest body)
  "Measure and return the time it takes evaluating BODY."
  `(let ((time (current-time)))
     ,@body
     (float-time (time-since time))))

;; Set garbage collection threshold to 1GB.
(setq gc-cons-threshold #x40000000)

;; When idle for 15sec run the GC no matter what.
(defvar k-gc-timer
  (run-with-idle-timer 15 t
                       (lambda ()
                         (message "Garbage Collector has run for %.06fsec"
                                  (k-time (garbage-collect))))))

As first I'm consciously setting the GC threshold to 1GB. Probably many will be surprised but this is just a polite way to say: "NEVER garbage collect unless we are potentially causing the OS to swap pages."

The second action I'm taking is to set a timer using run-with-idle-timer. That means that every time Emacs will be idle for 15 secs we'll garbage collect once. The assumption is that the probability that we are going to input a command exactly after 15 secs is rather low.

I believe this unconventional garbage collection strategy should be completely clear to the reader now.

You can tweak the two parameters as you prefer and experiment a bit with maybe less extreme values. If gc-cons-threshold is enough high to avoid GC during start-up also this will benefit from this setup. This exact value really depends on your Emacs configuration. In general Emacs will feel more responsive.

To conclude I believe the biggest advantage is probably that coming back from the coffee or just from time to time you'll see into the mini-buffer how much time you have saved. This will certainly boost your productivity and enthusiasm. In case this happens you'll forever be grateful to me.

Author: akrl

Created: 2019-05-11 Sat 12:19

Validate