Main menu:

Site search

Categories

Archive

Starting JägerMonkey

About 2 months ago, we started work on JägerMonkey, a new “baseline” method JIT compiler for SpiderMonkey (and Firefox). The reason we’re doing this is that TraceMonkey is very fast for code that traces well, but for code that doesn’t trace, we’re stuck with the interpreter, which is not fast. The JägerMonkey method JIT will provide a much better performance baseline, and tracing will continue to speed us up on code where it applies.

This week, we’ve been sprinting to bring up the basic compiler, and as of today, JägerMonkey implements enough JavaScript to run all of SunSpider in “Jäger mode” and is 18% faster than the interpreter. And we haven’t done that many optimizations yet–there are many more things we will do (see the wiki article).

(Update 28 Aug 2012: Valeria Aleksandrova has produced a Polish translation of this article.)
In the rest of this post, I’ll give a little more background on why we’re doing this, and a summary of what we’ve done so far.

Why JägerMonkey. TraceMonkey’s tracing JIT is very fast for code that it can JIT. For example, it is 9x faster than the interpreter on SunSpider’s math-cordic benchmark. But it can’t really trace a benchmark like date-format-tofte, which calls eval in its main loop, so tracing only yields a 5% speedup on that program. As David Anderson put it, TraceMonkey has rocket boosters, so it runs really fast when the boosters are on, but the boosters can’t always be turned on.

(See also the hacks article for much more background on how tracing works.)

There are many factors that can prevent the rockets from turning on, so there’s really no short description of the programs that don’t trace, but most of them fall into a few categories:

  • Programs with very branchy control flow. Tracing works by generating type-specialized native code for program paths. So if a program has 1000 paths in its hottest loop, TraceMonkey would have to generate 1000 paths to run it natively with tracing. But that would use up way too much memory for code, so instead TraceMonkey stops after a certain number of paths and falls back to the interpreter. Another problem with branchy code is that generating a trace takes time, so if there are many branches and each branch is run fewer times, TraceMonkey gets less benefit for the cost of compiling.
  • Programs with many type combinations. Because TraceMonkey generates type-specialized code, it must generate a separate trace for every type combination (mapping of variables to types) the program generates. If there are 1000 type combinations, we have the same problems we get with 1000 paths.
  • Programs that call eval in their hot loops. TraceMonkey needs to know all the variables and their types in order to generate type-specialized code. Because eval can potentially do anything, TraceMonkey must give up when it sees an eval. There are a few other language features and corner cases that TraceMonkey can’t trace for similar reasons.

These untraceable programs are a result of two basic design factors:

  • Trace JIT vs. method JIT. A method JIT compiles each statement in a method once, while a trace JIT may need to compile a statement many times if it is contained in many traces. So a method JIT isn’t hurt by branchy code.
  • Mandatory type specialization vs. type specialization lite. A JIT that always type specializes has trouble with code that uses too many type combinations, or special features like eval. A JIT that doesn’t type specialize doesn’t have those problems. A JIT that type specializes only a little bit, or only optionally, also avoids those problems.

Note that although a tracing JIT can either type specialize or not, and a method JIT can also type specialize or not, type specialization is a natural companion of tracing. Consider code like this:

  var x;
  if (z) {
    x = 3;
  } else {
    x = "hello";
  }
  var y = x + 77;

A trace JIT will compile two traces, which look something like this:

  // trace 1
  if (!z) exit this trace;
  x = 3;
  y = x + 77;

  // trace 2
  if (z) exit this trace
  x = "hello";
  y = x + 77;

Notice how the types of x and y are completely known, so it is relatively easy to completely type-specialize this code. Accordingly, TraceMonkey is designed to always type-specialize everything. On the other hand, a method JIT compiles the whole method just once, so the method JIT really can’t know the type of y, and must generate non-specialized code.

(But a method JIT can type-specialize, and a trace JIT doesn’t have to. For example, a trace JIT could choose to generate non-specialized code. But then the JIT becomes more complex–it needs a notion of “unknown” types and it needs separate code generation functions to handle those cases. And a method JIT could look ahead to see that there are only 2 different types, and generate two type-specialized cases. Or it could even decide to duplicate the assignment to y inside the branches so that it can be type-specialized. But again, this makes the JIT more complex than the basic non-specializing method JIT design.)

So, a type-specializing trace JIT generates really fast code, but can’t generate native code for the situations described above. Conversely, an optionally specializing method JIT generates moderately fast code, but can generate native code for any JavaScript program. So the two techniques are complementary–we want the method JIT to provide good baseline performance, and then for code that traces well, tracing will push performance even higher.

JägerMonkey so far. Now I’ll say a little more about what we’ve done so far on JägerMonkey.

The first thing we needed was a fast assembler to generate the native code. TraceMonkey has a native code compiler, nanojit, but we thought nanojit wasn’t ideal for JägerMonkey. Nanojit does a fair number of compiler backend optimizations, like dead store elimination and common subexpression elimination, which allows to generate faster code, but makes it take longer to generate that code. We don’t expect those optimizations to help much in the Jäger domain, so we wanted something simpler and faster.

We decided to import the assembler from Apple’s open-source Nitro JavaScript JIT. (Thanks, WebKit devs!) We know it’s simple and fast from looking at it before (I did measurements that showed it was very fast at compiling regular expressions), it’s open-source, and it’s well-designed C++, so it was a great fit. Julian Seward modified it to run with our build system and support libraries. It’s in our tree with the appropriate licensing, and we’re already using it to get that 18% speedup I mentioned before.

Another key component is the method JIT compiler itself, which David Anderson designed and started up. Right now it’s pretty basic but works well, so I don’t have a lot more to say about it right now. One interesting note is that David created a new function that does abstract interpretation of the bytecode in order to compute stack depths and incoming branch edges. The compiler uses the results to do some optimizations that gave us another 5% speedup or so.

Finally, as part of the JägerMonkey project, we are going to make a bunch of changes to the interpreter to make it more amenable to JIT optimization. The first change, done by Luke Wagner, was to simplify the stack the interpreter uses to store temporary values and JavaScript stack frames. Previously, stack frames were laid out in a linked list of memory chunks, which keeps stack memory usage very low, but complicates the allocation of new stack frames and addressing of variables and values stored on the stack. Luke changed it to use a single “slab o’ memory”, so allocating a new stack frame is just a size check and pointer increment, and values and variables are always at fixed offsets from the stack frame headers. This makes it easier to write the JIT and easier to generate simple, fast code to access stack values. We were pleasantly surprised to find that the stack rearrangement alone gave a 3-5% speedup, both in the interpreter and JägerMonkey.

At this point, everything’s looking good. The next step is to integrate JägerMonkey with tracing, so we can use them complementarily. We’ll also be continuing with the interpreter upgrades and simplifications. Finally, I’m going to try science to learn more about existing JavaScript code and how best to design JägerMonkey to run it fast.

(Link to Belorussian translation of this article, courtesy of Amanda Lynn.)

Comments

Pingback from Mozilla borrows from WebKit to build fast new JS engine « 567 Technology
Time: March 9, 2010, 7:09 am

[…] will continue to speed us up on code where it applies," wrote developer David Mandelin a blog entry about the new […]

Pingback from Firefox’s Next JavaScript Engine Will Borrow from WebKit [Firefox] | Geek News and Musings
Time: March 9, 2010, 7:20 am

[…] it’s starting to fall behind its competitors. To up its game, Firefox’s developers are building a new engine, dubbed JägerMonkey. Ars Technica writes that the new compiler uses some open-source WebKit code to get the job done, […]

Pingback from Firefox’s Next JavaScript Engine Will Borrow from WebKit [Firefox] | Digital Digg Blog
Time: March 9, 2010, 7:24 am

[…] it’s starting to fall behind its competitors. To up its game, Firefox’s developers are building a new engine, dubbed JägerMonkey. Ars Technica writes that the new compiler uses some open-source WebKit code to get the job done, […]

Pingback from Firefox’s Next JavaScript Engine Will Borrow from WebKit [Firefox] | Phatboi's Blog-Roll
Time: March 9, 2010, 7:24 am

[…] it’s starting to fall behind its competitors. To up its game, Firefox’s developers are building a new engine, dubbed JägerMonkey. Ars Technica writes that the new compiler uses some open-source WebKit code to get the job done, […]

Pingback from Firefox’s Next JavaScript Engine Will Borrow from WebKit [Firefox] | World Wide Web
Time: March 9, 2010, 7:58 am

[…] it’s starting to fall behind its competitors. To up its game, Firefox’s developers are building a new engine, dubbed JägerMonkey. Ars Technica writes that the new compiler uses some open-source WebKit code to get the job done, […]

Pingback from Firefox’s Next JavaScript Engine Will Borrow from WebKit [Firefox] · TechBlogger
Time: March 9, 2010, 8:00 am

[…] it’s starting to fall behind its competitors. To up its game, Firefox’s developers are building a new engine, dubbed JägerMonkey. Ars Technica writes that the new compiler uses some open-source WebKit code to get the job done, […]

Pingback from Firefox’s Next JavaScript Engine Will Borrow from WebKit [Firefox] | bruno trani dot info
Time: March 9, 2010, 8:02 am

[…] it’s starting to fall behind its competitors. To up its game, Firefox’s developers are building a new engine, dubbed JägerMonkey. Ars Technica writes that the new compiler uses some open-source WebKit code to get the job done, […]

Pingback from Firefox’s Next JavaScript Engine Will Borrow from WebKit [Firefox] | Hooked On 'Tronics
Time: March 9, 2010, 8:46 am

[…] it’s starting to fall behind its competitors. To up its game, Firefox’s developers are building a new engine, dubbed JägerMonkey. Ars Technica writes that the new compiler uses some open-source WebKit code to get the job done, […]

Pingback from Firefox’s Next JavaScript Engine Will Borrow From WebKit | Lifehacker Australia
Time: March 9, 2010, 10:02 am

[…] Mozilla’s home-brewed JavaScript engine for its Firefox browsers, TraceMonkey, has impressed us before, but in the raw benchmark game, it’s starting to fall behind its competitors. To up its game, Firefox’s developers are building a new engine, dubbed JägerMonkey. […]

Pingback from Firefox’s Next JavaScript Engine Will Borrow from WebKit [Firefox] | TechBlogs Today
Time: March 9, 2010, 12:44 pm

[…] it’s starting to fall behind its competitors. To up its game, Firefox’s developers are building a new engine, dubbed JägerMonkey. Ars Technica writes that the new compiler uses some open-source WebKit code to get the job done, […]

Pingback from Firefox’s Next JavaScript Engine Will Borrow from WebKit [Firefox] | Tech News From All Over The Net
Time: March 9, 2010, 1:09 pm

[…] it’s starting to fall behind its competitors. To up its game, Firefox’s developers are building a new engine, dubbed JägerMonkey. Ars Technica writes that the new compiler uses some open-source WebKit code to get the job done, […]

Comment from Ricardo
Time: March 10, 2010, 5:49 am

Carakan is better !!!! Opera rules !!!!

Pingback from TecNews: Noticias Tecnofagia
Time: March 10, 2010, 9:32 am

[…] Com o objetivo de modificar essa situação, os desenvolvedores do Firefox trabalham em um novo mecanismo JavaScript, que recebeu o nome JägerMonkey. […]

Pingback from The Far Edge » Blog Archive » Firefox’s Next JavaScript Engine Will Borrow from WebKit [Firefox]
Time: March 10, 2010, 2:21 pm

[…] it’s starting to fall behind its competitors. To up its game, Firefox’s developers are building a new engine, dubbed JägerMonkey. Ars Technica writes that the new compiler uses some open-source WebKit code to get the job done, […]

Pingback from Mozilla’s comeback in Javascript performance « Antony Williams' blog
Time: March 18, 2010, 11:08 am

[…] how can Firefox stay relevant?By building JägerMonkey.According to Firefox developers David Mandelin, “The reason we’re [building JägerMonkey] is that TraceMonkey is very fast for code […]

Pingback from Austin Kim » Firefox’s Next JavaScript Engine Will Borrow from WebKit [Firefox]
Time: March 22, 2010, 10:34 pm

[…] it’s starting to fall behind its competitors. To up its game, Firefox’s developers are building a new engine, dubbed JägerMonkey. Ars Technica writes that the new compiler uses some open-source WebKit code to get the job done, […]

Pingback from New JavaScript engine of Firefox « GNU/LINUX
Time: April 18, 2010, 1:21 am

[…] improvement They made the new engine enhancements include many aspects. If you are interested, Mandelin The blog article is worth reading. Simply put, only use the J ä gerMonkey Firefox engine will be more […]

Pingback from Firefox 4: szybszy, przyjaźniejszy i bardziej prywatny! | Blog IT – ittechblog.pl
Time: May 11, 2010, 8:28 am

[…] kodu JavaScript (nowy silnik JägerMonkey), ogólna poprawa wydajności, optymalizacje. Były plany, by skorzystać z silnika JS obecnego w […]

Pingback from 5 Exciting Changes Coming to Firefox 4 | Geek Technica
Time: June 28, 2010, 7:20 am

[…] but a long shot.Firefox currently uses nanojit for its native code generator and will be moving to Webkit’s JSCore engine for Firefox 4. Which combined with Tracemonkey’s powerful optimization should give them a better performance […]

Pingback from Tweets that mention David Mandelin’s blog » Starting JägerMonkey — Topsy.com
Time: July 28, 2010, 8:53 pm

[…] This post was mentioned on Twitter by Bao, Mackenzie. Mackenzie said: http://bit.ly/9sIzY6 David Mandelin […]

Pingback from JägerMonkey의 JavaScript 성능 향상 ✩ Mozilla 웹 기술 블로그
Time: August 12, 2010, 4:32 pm

[…] 자세한 정보를 원하시는 분은 David Mandelin과 David Anderson의 블로그와 새로운 엔진 프로젝트 페이지를 […]

Pingback from RPW 01/03/10: firefox JavaScript, opera 10.5, referencement flash, règles de performances web | BrainCracking – Veille technologique sur les applications Web
Time: August 24, 2010, 9:10 am

[…] war : preview de la prochaine mouture du compilo JS de Firefox (et une autre ici), qui d’après des […]