Emacs Lisp is Probably Faster Than What You Think
December 15, 2022
Emacs Lisp is the native language of GNU Emacs, an interpreted language. We are used to think that interpreted language are slow. Yes, they are indeed slower than compiled languages like C and Assembly. But with the latest additions like JIT, native-compilation and various optimizations, they are catching up with compiled languages.
I got the first surprise when I tested GNU Guile. It is really fast, as fast as C in many cases, when JIT is enabled. However, Emacs Lisp is not so fast, but it's OK for most use cases.
But two weeks ago, I got a surprise with Emacs Lisp. I was
implementing multi-column character support in Eat. I found an
efficient way implement this while not breaking existing code. But it
had one problem, as you might have already guessed, I needed to find
all multi-column and zero column (yes, there are some) characters in
the output. At first, I used a binary search-like algorithm using
string-width
, as I thought C code is fast. Not only it produced too
much garbage, it also didn't work for obvious reason. Understanding
the problem, I decided to use char-width
. After the initial
implementation, I thought it was going to make Eat much slow, since it
needs to check every character of the output string. But to my utter
surprise, it didn't slow down Eat more than 3-4%. So I happily
committed and published it.
Two days ago, learning from the previous incident, I thought to try replacing the regular expressions based output parser with a parses output one character at a time. After implementing it, I tested it. But it was not measurably faster than the old code, so I decided to abandon the ready to commit change, out of the fear of breaking programs.
But today, I thought to give it another change. My browser was open when I tested back then. The browser usually takes up all my memory, slowing down everything on my 4 GB RAM computer. But this time the browser was not running. After testing, I found that it is actually about 5% faster when Eat is native-compiled, which was more than I expected. That means the Emacs Lisp based parser is faster than thea simple regular expression based parser, despite regular expressions are implemented in C. As a bonus, the change didn't make the code more complicated (I think it simplified the code instead). Being happy with it, I decided to commit and publish it.
So the conclusion is that Emacs Lisp can be faster than C when the code is efficient and optimized enough. So before you blame Emacs for your slow Emacs Lisp code, first check what you wrote!