Squirrels and Monkeys - The faster future of JavaScript
Dj Walker-Morgan
JavaScript has spent the summer of 2008 in the garage being tuned up, with two different approaches to increasing the performance of the scripting language behind AJAX web applications and dynamic content on the Internet. TraceMonkey from Mozilla and SquirrelFish from the Webkit team at Apple are both replacement engines for the inside of the JavaScript engine found within web browsers.
The need to make JavaScript faster has come from the language's increased importance as a fundamental building block of the open Internet. When JavaScript arrived in the nineties, the decision was made somewhere in marketing that JavaScript would do the light lifting within web pages, with the heavy work being handed off to Java applets. It didn't work out that way though; JavaScript came to be used to manipulate the pages on the web and Java applets just faded away.
Now JavaScript, thanks to AJAX technologies, has become essential as the scripting glue of the web. With more people using it, developers have been pushing the limits of what JavaScript is capable of, so much so that it could provide a competitor to Adobe's Flash, which took over where Java applets failed as the way to do dynamic graphical content on the web page. To get to the next stage in its evolution, JavaScript had to become faster.
Although JavaScript has been sped up over the years, it has remained a traditionally interpreted language, where the text in the code is parsed into what is called a syntax tree of opcodes - these opcodes typically represent the keywords within the language - and acted on with no intermediate steps. Typically, an interpreter developer can write a virtual machine to process these opcodes, stepping through the syntax tree as needed.
There has been a lot of research into how to make interpreted languages run faster over the years, and now some of this research is being applied to JavaScript.
SquirrelFish is Apple's new WebKit JavaScript interpreter. SquirrelFish works by parsing JavaScript code from the syntax tree and lazily compiling it into its own lower level bytecode. It is this bytecode that is executed and retained in memory, so that if the same code is executed again, there is no repeated compilation. This is only, of course, part of the optimisation process. The bytecode itself is designed to work with registers - built-in variables within the processors – rather than the more traditional stack where variables are stored in main memory. This removes a lot of memory access from the execution of the code which, despite caches and fast memory buses, is still quite costly. The first release of SquirrelFish sped up WebKit's already reasonably fast execution by 1.6 times. The big advantage with the SquirrelFish approach is that the changes they have made remain portable across different processors.
But, what if you could skip executing bytecode, and go straight to the processor's machine code, forsaking portability for performance. Mozilla developers have been working on how to improve their JavaScript performance and this is where TraceMonkey comes in. Mozilla has in the past used a JavaScript interpreter called SpiderMonkey in its browsers; SpiderMonkey carried a decade's worth of optimisation hacks and "fat" opcodes, and the Mozilla project were considering throwing it out and replacing it with Adobe's Tamarin project.
Tamarin is Adobe's ActionScript engine aimed at executing the now-not-happening ECMAScript 4, which Adobe contributed to Mozilla. Tamarin uses just in time (JIT) compilation to increase performance; as a function or method is being interpreted for the first time, the system compiles that code and executes the compiled version. The drawback of this approach, as with early Java JIT implementations, is that compilation takes time. In a recent branch of Tamarin, Abobe looked at how to optimise the compilation of code by a process called tracing, where the path of code execution is traced and, where it is valuable to compile code, that code is then compiled. This project was called Tamarin-Tracing.
Throwing out SpiderMonkey to replace it with Tamarin was regarded as problematic, so some Mozilla developers took a different approach – adding tracing to SpiderMonkey. They used the back-end of Tamarin-Tracing, called nanojit, and incorporated it into the existing SpiderMonkey framework to create TraceMonkey. TraceMonkey watches the bytecode, looking specifically at loops in the code, and when appropriate it can trigger compilation of the code in that loop. Unlike SquirrelFish, the code is compiled to the machine code of the underlying processor. What this means in practice is huge wins on code where there are a lot of intensive calculations within loops. Less so, though where there is code that does not repeat. In a comparison between SquirrelFish and TraceMonkey, TraceMonkey was twelve times faster than SquirrelFish on some tests, yet was approaching half the speed of SquirrelFish in others. The other issue with TraceMonkey is portability between processors; nanojit has to be able to generate machine code for the CPU it is running on, and currently supports only x86 and ARM architectures; this provides good coverage for the current deployed processors inside machines today so doesn't provide an immediate worry.
For both approaches, SquirrelFish and TraceMonkey, this is early days in their development and both still have numerous low-hanging fruit, relatively simple optimisations, which they can apply. SquirrelFish could be boosted by native code generation; Apple are heavily involved with LLVM and the developments there may dovetail with SquirrelFish's bytecode to generate native code. TraceMonkey could be boosted by tuning the code generation or the higher level Javascript processing. The important thing though is that JavaScript is getting faster, potentially much faster.
It is these performance improvements that, in tandem with other developments in browsers, that could give Adobe's Flash a hard time in the future. The rendering engines in browsers such as Firefox and Safari are getting smarter; it's not just about text and images laid out on a page any more. A modern renderer can handle multiple overlaid layers of graphical content, animate objects and render scalable vector graphics. In combination with faster JavaScript engines, the native run time capabilities of browsers could equal that of Flash, currently lacking only the development tools that could make a Flash designer move away from Flash. For web application developers though, they already have a toolchain that can make use of these developments, allowing them to make graphically richer applications, and with the boosted performance of JavaScript, they can spend less time optimising and tuning code. The arrival of TraceMonkey and SquirrelFish heralds a future where the rich internet application is delivered by the browser, not by plugins attached to the browser.