Apple integrates LLVM compiler to boost WebKit JavaScript performance

Since compilers already make fast code, why not take advantage?

We looked last week at some of the things that can influence the speed of programming languages and how different approaches to running code can influence program performance. Some work described by Apple this week shows how techniques common in one language can be applied to another.

Modern browsers use a scheme called just-in-time compilation to convert JavaScript into executable code and achieve high performance when they do so. As the optimization process has become more advanced and performance improved, Apple's WebKit developers have realized that they're increasingly building the same capabilities as traditional compiler developers have already built. Feeling that's rather wasteful, they've decided to stop building their own and instead leverage the optimization capabilities of the LLVM compiler.

The work was described earlier this week in a blog post. The WebKit JavaScript engine previously had three different stages that achieved different performance levels for the scripts they ran. The first stage was an interpreter that ran script programs without compiling them. This allows scripts to run "instantly" without having to wait for a compiler.

If a JavaScript function is called more than six times, or loops more than 100 times, it gets fed into the next stage: a basic JIT compiler that produces executable code. This doesn't optimize heavily, but it removes some of the overheads that the interpreter has to produce code in the ballpark of ten times faster than the interpreter.

If the function is then called more than 66 times, or loops more than 1,000 times, it gets fed into the third stage, a JIT compiler that actually spends a relatively long time to optimize the code. The third stage output is about three times faster than the basic JIT, or 30 times faster than the interpreter. It would take too long to optimize every function with this compiler, which is why this staged approach is used.

Though that third stage is much faster than the interpreter, it still doesn't match a typical C or C++ compiler for low-level optimizations: figuring out which values to store into which registers, for example. The WebKit developers wanted to add a fourth stage to perform this kind of work, but while they thought that writing it from scratch would be "super fun," it'd also be a lot of work. A lot of work that existing C and C++ compilers already do. Hence, they decided to investigate the use of LLVM's code generation and optimization.

The LLVM-based fourth tier is called FTL, for Fourth Tier LLVM (and, of course, faster than light). It shares some portions with the third stage, since the third stage already does important work for handling JavaScript's dynamic nature but has a different code generating portion.

The result is a healthy performance boost. FTL produces code that is more than 40 times faster than the interpreter, with benchmarks taking about a third less time to run than the old three-tier system.

One of the downsides of the LLVM compiler is that it's, relatively speaking, quite slow. This isn't altogether surprising, as LLVM is designed to optimize well, rather than optimize quickly, and for ahead-of-time compilation of the kind used for C and C++ it's worth taking the extra time to do a good job. To handle that, WebKit only uses FTL for functions that are taking a long time to run (10 milliseconds) or being called an awful lot—to ensure that it's really worth using FTL—and then it runs FTL on a background thread. It doesn't have to wait for that thread to finish, as it can simply use the code from the third stage in the meantime.

FTL is currently available in WebKit nightly builds and enabled by default.

A decade ago, JavaScript was only ever an interpreted language, and it was quite slow because of it. Since then, it has risen in importance and become the focus of considerable development effort to improve its performance. The use of LLVM and the integration of a traditional compiler's optimizer show how features common to one set of languages can be used in others, making the distinctions between different kinds of language fuzzy.

It's unlikely that Apple will be the only browser developer to do this kind of thing. Browser developers like to crow about the performance of their JavaScript engines, and the likes of Microsoft and Google won't want to let Apple take the lead in this field.