Improving JavaScript performance has been a hot topic in recent years. The ubiquity of the World Wide Web, and web runtimes in particular, across all kind of devices in addition to the increasing complexity of applications written in JavaScript have drawn attention of the industrial and research communities towards the improvement of JavaScript performance.
Our research focus on the performance aspects of this problem. We propose a novel solution based on meta-programming and code rewriting, show some preliminary results based on a small set of experiments, and then review other approaches and compare them with our proposal.
Most of the work to boost the performance of programs written in JavaScript is focused on improving the compiler/execution infrastructure. Such approach is implemented by the Safari SquirrelFish runtime, the just in time (JIT) compiler Google V8, and applied to the trace-based optimizations by the Mozilla team. A different approach is Mozilla’s asm.js, a subset of JavaScript from which highly efficient code can be generated on the fly.
Our work, on the other hand, explores the feasibility of improving performance by using source code rewriting techniques at compile time, well before the source code reaches the JavaScript runtime. We found several cases where JavaScript performance is highly sensitive to the way in which the programmer writes the code.
For example, using the for-in statement to iterate an array object is at least 25 times slower than using a standard C-style-for to iterate the same array. Even more remarkable, different APIs generating the same, or semantically similar, results can show a big difference in execution times. For example, using the API document.getElementByTagName to find elements in the DOM can be 200 times faster than using the API document.querySelectorAll.
Therefore, we propose to improve the performance of JavaScript programs by analysing this kind of cases, which are out of reach of JIT compilers and runtimes. To test our hypothesis we initially identified a number of patterns amenable to being optimized and then implemented a set of simple experiments to check the performance gains. Once we showed that replacing slow patterns with the fast ones provided measurable performance benefits, we designed and implemented a general purpose tool to easily automate this refactoring. The tool, named PumaScript, is a JavaScript dialect extended with program introspection and rewriting capabilities. Finally, by using PumaScript we are able of implementing meta-functions to automatically rewrite slow code with faster, semantically equivalent code.
PumaScript’s main feature is the support for meta-functions, a mechanism similar to the programmable macro-expansion systems available in Lisp, Ruby, and other programming languages. Like other macro systems, PumaScript meta-functions can expand caller expressions inline. When called, a meta-function takes the decorated Abstract Syntax Tree (AST) of the arguments and returns an AST to be used as a replacement for the caller expression.
PumaScript meta-functions can execute any arbitrary computation, including calling other normal functions or meta-functions. Additionally, all meta-functions have access to special intrinsic functions which provide access to the program AST for introspection and re-writing of any portion of it.