Flaky Goodness

Speeding up Emacs Lisp functions by disabling garbage collection

August 28, 2024

Emacs Lisp has nice built-in profiling tools but if you’re just curious to see how long a function takes to run, you can call it from inside benchmark-run:

(benchmark-run (my-slow-function))

This will output a list with three elements. The first is total runtime in seconds. The second element is the number of garbage collections required during execution, and finally the time used by the garbage collector.

For example, I can see how long it takes to export my static blog site as follows:

(benchmark-run (export-my-blog))

(7.007418 45 3.120531000000028)

It is taking 7 seconds total of which 3 seconds are consumed by 45 garbage collection passes.

Can we save some time by opting out of garbage collection during this process?

Here is a wrapper function that temporarily disables garbage collection while an arbitrary function is called. We do this by increasing the garbage collection threshold to a very large number.

(defun without-gc (&rest args)
  (let ((gc-cons-threshold most-positive-fixnum))
    (apply args)))

Now we can call:

(benchmark-run (without-gc #'export-my-blog))

(4.881724999999999 1 1.7296989999999823)

Down to 4.9 seconds, only 1.7 of which are used by a single garbage collection pass (presumably at the end of execution).

It is worth noting that opting out of garbage collection might not make sense for extremely long running or memory intensive processes: you don’t want Emacs to accidentally gobble up all of your available memory. Still, this is a nice optimization for those Emacs operations that seem to drag on just a bit too long.