In August 2008, Mozilla introduced TraceMonkey. The new engine, which we shipped in Firefox 3.5, heralded a new era of performance to build the next generation of web browsers and web applications. Just after the introduction of our new engine Google introduced V8 with Chrome. Apple also introduced their own engine to use in Safari, and even Opera has a new engine that they’ve introduced with their latest browser beta.
As a direct result of these new engines we’ve started to see new types of applications start to emerge. People experimenting with bringing Processing to the web, people experimenting with real-time audio manipulation, games and many other things. (For some good examples have a look at our list of Canvas demos.)
We’ve learned two things at Mozilla about how our JavaScript engine interacts with these new applications:
- That the approach that we’ve taken with tracing tends to interact poorly with certain styles of code. (That NES game example above, for example, tends to perform very badly in our engine – it’s essentially a giant switch statement.)
- That when we’re able to “stay on trace” (more on this later) TraceMonkey wins against every other engine.
Mozilla’s engine is fundamentally different than every other engine: everyone else uses what’s called a “method-based JIT”. That is, they take all incoming JS code, compile it to machine code and then execute it. Firefox uses a “tracing JIT.” We interpret all incoming JS code and record as we’re interpreting it. When we detect a hot path, we turn that into machine code and then execute that inner part. (For more background on tracing, see this post on hacks from last year.)
The downside of the tracing JIT is that we have to switch back and forth between the interpreter and the machine code whenever we reach certain conditions. When we have to jump back from machine code to the interpreter this is what we call being “knocked off trace.” The interpreter is, of course, much slower than running native machine code. And it turns out that happens a lot – more than anyone expected.
So what we’re doing in our 2nd generation engine is to combine the best elements of both approaches:
- We’re using some chunks of the WebKit JS engine and building a full-method JIT to execute JavaScript code. This should get us fast baseline JS performance like the other engines. And most important, it will be consistent – no more jumping on and off trace and spending a huge amount of time in interpreted code.
- We’ll be bolting our tracing engine into the back of that machine code to generate super-fast code for inner loops. This means that we’ll be able to still have the advantages of a tracing engine with the consistency of the method-based JIT.
This work is still in the super-early stages, to the point where it’s not even worth demoing, but we thought it would be worth posting about so people understand the basics of what’s going on.
You can find more information about this on David Mandelin‘s and David Anderson‘s weblogs as well as the project page for the the new engine.
25 comments