Although I'm not technically a Microsoft employee anymore (I left Microsoft Research, Cambridge, at the end of 2006), I still consider myself friendly with Microsoft and have, for various reasons (including stock), a certain residual interest in the well-being of the company. Also, to be honest, while I didn't regret leaving Microsoft Research all too much (it wasn't quite the MSR I joined in 1998), I liked being with Microsoft, and would, if the circumstances were right, return without too much hesitation. That's why I'll keep adding to this blog until it gets shut down for the reasons stated in the first sentence.

In the course of my being "friendly with Microsoft", I read somewhere that when IE8 was released in beta 2 somebody said that performance benchmarking(...) was a secondary priority. Then Google's Chrome browser and Firefox started bragging about their JavaScript performance and hitting one another over their heads with benchmarks that proved their respective engines to be orders of magnitudes faster than anything seen before, including IE8, and I thought the bit about "secondary priority" a bit awkward. I'm not a compiler expert, but I have some experience in that field that enables me to make a few mildly informed guesses about what you can do - even without too much effort. And even without making performance first priority, there's something called state of the art, and both Chrome and Firefox have just established what that entails. And it rhymes with "native code".

Firefox (TraceMonkey)

Firefox' next generation JavaScript implementation employs a technique called Trace Trees to compile JavaScript "just-in-time" into native code. The unit of compilation is neither a function (as it is common in Java JITs), nor a code file (as in C), but a trace, i.e. a sequence of instructions that the interpreter has been executing several times. Usually, this constitutes the body of a loop, or, more precisely, a specific path through a loop. Thus, frequently taken paths through a JavaScript program will be executed in native code, while the remainder is still interpreted. Specific properties of such a trace render it suitable for certain optimizations, mainly to eliminate common sub-expressions. The bulk of the speedup, however, is due to the execution of significant (or important, rather) parts of the program in native code.

Chrome (V8)

As far as I understand, the V8 runtime features a compiler that compiles all JavaScript code (the contents of an external file or the stuff between <script> tags) into native code. That's where the bulk of its perfomance is coming from: there's no interpreter, just native code. Furthermore, it employs an optimization technique to speed up member accesses of objects (methods or fields), by creating some sort of "classes" behind the scene whenever an object's field is written. By constructing these "classes" the compiler can generate (static) offsets to access fields instead of requiring runtime-lookups in the object's dictionary.

SquirrelFish Extreme

Webkit's "extreme" JavaScript engine employs several techniques to improve performance, including bytecode optimizations (within and across bytecodes), a so called Polymorphic Inline Cache to speed up member accesses similar to the technique employed by the V8 engine above, and also a context threaded JIT compiler. While you can get significant improvements by optimizing interpreted bytecode, both by optimizing the codes themselves, and by providing special bytecodes for frequent bytecode combinations, I suspect that the biggest performance increment is due to the JIT compiler and, hence, parts of the program running in native code. Note that compiling to native code above all gets rid of the interpreter loop which constitutes a significant portion of the instructions a processor needs to execute.

IE8

Despite what they appear to perceive as impressive performance gains in IE8's (beta 2) JavaScript engine, compared to Chrome's V8 or Firefox' TraceMonkey, these gains cannot reasonably be called anything but fiddling around the edges. Unless the JavaScript engine used in IE (and elsewhere) employs some sort of compilation to native code, it will always lag behind its competitors with respect to performance. From what I gather in their Channel9 appearance they have made improvements in bytecode execution, but their main targets were JavaScript native objects (Array, String, ...) and JavaScript-DOM-interaction. That leaves the matter of compilation to native code unresolved. With the formidable competition racing for the top spot in their tailored-for-their-engine benchmarks, I believe it's time for Microsoft to do what it does best: compete to kill win!

To that end, I'm proposing adding a one-pass compiler to Microsoft's JavaScript engine. That's neat and simple and should restore Microsoft's standing within the JavaScript engine competition. There's really no reason not to try, but as I expect objections galore, here's a (p)rebuttal of the most common ones:

  • Compiling all JavaScript to native code is too slow (startup-time wise)
    Wrong. First, V8 does it, too, and it beats the hell out of IE's JS engine. Also, as any compiler builder knows, a non-optimizing compiler spends easily 60% of the compilation time in the tokenizer, and tokenizing is what you have to do anyway, even in an interpreter. Nonetheless, I remember one of the Oberon compilers built by one of the graduate students at ETH for a HP Unix workstation that compiled itself in some 2.2 seconds. That's 2.2 seconds to compile the whole compiler, and that was 12 years ago when a 133 MHz PC was considered state of the art.
  • Building a compiler is complicated. You cannot hurry it
    That's why I'm proposing a single-pass compiler. Get one of the Visual Studio compiler people(preferably one of the back-end crowd), hand her the source code of the JavaScript engine which, I presume, generates some sort of intermediary code to be interpreted, and have her change it to produce native code instead. I'd be surprised if it takes her more than 4 weeks to do it. It needn't be optimized. Above Oberon compiler wasn't, and... well ... 2.2 seconds!!!
  • If we even think of doing it, we should do it right
    Oh, well. That's the pretext not to do it at all. So much for the 80/20 rule. Couldn't they just do it first, and then do it right? I guess a simple single-pass compiler would bring IE within 80 to 90 per cent of V8, and perhaps equal TraceMonkey in generic, i.e. non-optimized-for-TraceMonkey benchmarks, because, after all, TM compiles only parts of the program, albeit these maybe slightly better. As I've highlighted above, the bulk of the performance of above engines can be attributed to their running native code, and not necessarily the kinds of extra optimizations that each performs in addition (although these can be significant, too).

So, even if the actual JavaScript performance may not be the most significant contributor to the overall performance of an AJAX application (DOM access, string performance, etc. are perhaps equally significant) I tend to consider this a matter of pride, and would like my engine to be the fastest on the planet, or at least within the league of all competitors'. Hence my challenge. Any takers?