The Mastering Emacs ebook cover art

Emacs 28 Edition is out now!

Read a Free Sample

Learn More

Speed up Emacs with libjansson and native elisp compilation

Native compilation is ship-shape and Bristol fashion, and available in Emacs 28 and onwards. But what does that mean for you? A much faster Emacs experience! You'll have to compile Emacs yourself to benefit from this, and I'll show you how.
Updated for emacs 28

Cyril Northcote Parkinson wrote in his book Parkinson’s Law that “work expands so as to fill the time available for its completion.” If you squint your eyes, you’ll see that this is also the case for modern software, and in particular Emacs.

For every incrementation in performance, there will be a new gizmo to dial it back down again. Common sources of performance bottlenecks include completion frameworks; the Emacs client libraries that talk to Language Servers; and other highly trafficked code paths used widely in Emacs’s many packages.

There are some ministrations that balm the performance pains, though: Emacs 27 now supports libjansson, a C library for working with JSON. If you’re a heavy user of lsp-mode or eglot then I highly recommend you upgrade for that reason alone.

As of Emacs 28 you can now benefit from the hard work by Andrea Corallo who discovered that you can use GCC to compile Emacs Lisp. That idea graced the minds of many smart Emacs hackers in the past, but the work of actually making it happen took years of Andrea’s time! It’s an incredible feat of engineering.

The only teensy-tiny downside is the effort of having to compile Emacs yourself, as it’s unlikely you have the tools installed for Emacs’s configure system to automatically enable native compilation. But, rest assured, it’s really quite easy to get working on Linux. (On other platforms, the level of pain required ranges from a light pinch to the vice-like jaw of a bear.)

Common Errors

So if you encounter Does Emacs have native lisp compiler? no during a compile step then your Emacs is not compiled with native compilation. Ditto if M-: (native-comp-available-p) returns nil.

Another common error is elisp native compiler was requested, but libgccjit was not found. and emacs was not built with native compilation support which is much the same reason. That’s almost always because libgccjit is either not installed or you haven’t paired up the right gcc version with the right libgccjit library – but that’s also easy to solve, as you’ll see below.

Enabling Native Compilation on Ubuntu

git clone https://git.savannah.gnu.org/git/emacs.git
apt-get update

First update your package repository lists and clone Emacs. It’ll default to master which is the bleeding edge of Emacs. Most of us that without trouble, but if you do, roll back a few revisions or check out a tagged release.

Next, you’ll need a couple of packages. You probably have all of them already, but just in case:

apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

Now we need to add a ppa that contains libgccjit. That is the library that Emacs needs for this to work. I’m also installing gcc-10 (but use whatever the latest version on your system is) as must have the same version of gcc and libgccjit installed.

Now you need to edit /etc/apt/sources.list and uncomment all the deb-src entries. We’ll need them as a shortcut to installing all the specific dependencies Emacs needs, in addition to the specialist ones to get native compilation working.

Once you have done that, you can install emacs’s build dependencies:

add-apt-repository ppa:ubuntu-toolchain-r/ppa
apt-get update
apt-get install gcc-10 libgccjit0 libgccjit-10-dev
apt-get build-dep emacs

If you want libjansson for fast JSON (you do!) you’ll need this also:

apt-get install libjansson4 libjansson-dev

Now you need to tell make what C compiler to use. As I installed gcc-10 earlier, that is what I specify in CC:

export CC="gcc-10"

Now you can do the usual configure-make rigmarole:

cd emacs
./autogen.sh
./configure --without-compress-install --with-native-compilation --with-json --with-mailutils

I strongly recommend you do ./configure --help and ensure you pick the options you want. But this is usually good enough to get you started.

Now build Emacs:

make -j 4
make install

The make install command may need root to install to your /usr/local directories.

And you’re done!

Dockerfile Example

FROM ubuntu:20.04

# We assume the git repo's cloned outside and copied in, instead of
# cloning it in here. But that works, too.
WORKDIR /opt
COPY . /opt

LABEL MAINTAINER "Mickey Petersen at mastering emacs"

# Needed for add-apt-repository, et al.
#
# If you're installing this outside Docker you may not need this.
RUN apt-get update \
        && apt-get install -y \
        apt-transport-https \
        ca-certificates \
        curl \
        gnupg-agent \
        software-properties-common

# Needed for gcc-10 and the build process.
RUN add-apt-repository ppa:ubuntu-toolchain-r/ppa \
        && apt-get update -y \
        && apt-get install -y gcc-10 libgccjit0 libgccjit-10-dev

# Needed for fast JSON and the configure step
RUN apt-get install -y libjansson4 libjansson-dev git

# Shut up debconf as it'll fuss over postfix for no good reason
# otherwise. If you're doing this outside Docker, you do not need to
# do this.
ENV DEBIAN_FRONTEND=noninteractive

# Cheats' way of ensuring we get all the build deps for Emacs without
# specifying them ourselves. Enable source packages then tell apt to
# get all the deps for whatever Emacs version Ubuntu supports by
# default.
RUN sed -i 's/# deb-src/deb-src/' /etc/apt/sources.list \
    && apt-get update \
    && apt-get build-dep -y emacs

# Needed for compiling libgccjit or we'll get cryptic error messages
# about failing smoke tests.
ENV CC="gcc-10"

# Configure and run
RUN ./autogen.sh \
        && ./configure --with-native-compilation --with-mailutils

ENV JOBS=2
RUN make -j ${JOBS} && make install

ENTRYPOINT ["emacs"]

The whole process may take a while. Native compilation is, as of Emacs 28.1, automatic, and runs in the background. There is nothing for you to do to make it work as it will compile stuff in the background while you’re using Emacs.

To test that both the fast JSON and native compilation is working you can evaluate the following elisp in Emacs:

(if (and (fboundp 'native-comp-available-p)
       (native-comp-available-p))
  (message "Native compilation is available")
(message "Native complation is *not* available"))

And for the JSON:

(if (functionp 'json-serialize)
  (message "Native JSON is available")
(message "Native JSON is *not* available"))

And.. that’s that. Enjoy your souped-up Emacs.

Further Reading

Have you read my Reading Guide yet? It's a curated guide to most of my articles and I guarantee you'll learn something whether you're a beginner or an expert. And why not check out my book?

Subscribe to the Mastering Emacs newsletter

I write infrequently, so go on — sign up and receive an e-mail when I write new articles