Compiling JavaScript for performance

Javascript performance tipsAs the reader already knows, computers don’t speak JavaScript. They speak their own language (machine code). Sadly, there is not just one machine language; there are several, and they are subject to change.In order to run a JavaScript program, the program must be translated into the client machine’s machine code. No matter how much we optimize the translation process, if it happens while the end-user is sitting at the machine waiting for a web page, then we have a performance issue. Ideally, the translation process should occur before the user requests the page.Today’s article discusses some of the performance issues involved in translating JavaScript to machine code.

When & Where

Interpreting and compiling are the same thing. The only difference is when they happen. Compiling happens before the program is loaded and executed. Interpreting happens on-the-fly, line-by-line while the program is executing. If an interpreted program seems slow, the translation process is partly to blame. However, if a program is compiled before the web page is requested, the translation process cannot be blamed.There are four approaches to translating JavaScript into machine code:

  1. Server-Side Build-Time Compilation: The server translates the JavaScript into the target machine’s native language before the page is requested (e.g., at build time) and downloads the machine code instead of the JavaScript code.
  2. Server-Side Request-Time Compilation: The server translates the JavaScript into the target machine’s native language after the page is requested, but before it is sent to the client machine. The server sends machine code, not JavaScript, to the client.
  3. Client-Side Compilation: The server sends the JavaScript code to the client, and the client translates it into machine code before executing it.
  4. Client-Side Interpretation: The server sends the JavaScript code to the client, and the client interprets it line-by-line, on-the-fly as it executes.

JavaScript Performance

For the best possible performance, JavaScript’s translation process must occur before the web page is requested, not while it is being processed on the back-end, downloaded, or rendered.Most existing websites use client-side interpretation (#4 above). However, this is drawing fire as one of the key performance issues facing these websites. In fact, this is the impetus behind the article you are now reading.Server-side build-time compilation (#1 above) is expected to deliver the best performance because it is the only one that completes the translation process before the web page is requested. The other three all negatively impact performance in some way.

JavaScript Security

Would you execute unfettered machine code that was downloaded from somewhere on the Internet? For security reasons, browsers limit the functionality available to web pages (e.g., access to storage devices is extremely limited). Raw machine code has none of those limitations.Browsers could be enhanced to inspect machine code before allowing it to execute. However, this is akin to running a comprehensive antivirus program on every web page, which could conceivably perform worse than what we do today. [Perhaps it’s time to build a prototype and try it out.]Of course, every browser in the world would need to be updated every time a new security vulnerability is discovered. This is the same situation we face today with operating systems, which require frequent security patches. If any machine does not install new security updates as soon as they are released, the probability of it suffering a security breach increases moment by moment.Each new update will break some existing web pages, which is a good thing for the bad web pages, but a big problem for the innocent ones.If the server compiles the JavaScript into machine code (#1 and #2 above), then downloads the machine code, something must be done to address these security concerns.

Cost

Creating prototypes, testing, negotiating consensus, and creating standards are all time-consuming. Creating browsers that download machine code is a costly endeavour.On the other hand, server-side translation requires more ongoing effort. As each web page is built and transferred to production, it would have to be translated into every machine language of every client that may access it.Cost alone is not sufficient reason to avoid this development effort. It must be compared to the benefits to be obtained. It is doubtful that corporations alone will move forward without significant help from the open-source communities. Corporations see benefit in protecting intellectual property; the open-source communities see benefit in free and easy access to infrastructure (and other) algorithms.

Today’s Best Approach

Today’s best approach is a cooperative arrangement between server and client. The server compiles the JavaScript program into an in-between language, opcode. The client then translates it from opcode to its own native language. This is better than interpreting JavaScript because opcode can be translated into machine code more quickly than JavaScript can, and opcode retains the browser’s built-in security features.Server-side solutions that translate from JavaScript to opcode every time a page is requested are not as good as they can be. The translation process should happen at build time, not when the page is requested.Browsers that cache opcode need to take the next step. They need to cache machine code instead. If they cache opcode, they will have to translate from opcode to machine code every time they load a page.

Caching

The above discussion indicates that machine code should be cached within the client. If it is not cached, or if it is cached as JavaScript or opcode, then its performance can be improved. If it is cached as machine code, every hit on the cache bypasses language translation completely.Whether caching JavaScript, opcode, or machine code, every web developer needs to set expiry dates as far into the future as possible to maximize the website’s use of caching.

Conclusion

JavaScript is an interpreted language. Interpreted programs are slower than their compiled counterparts. If JavaScript can be fully translated into machine code before the page is requested, the webapp will run faster.Pre-compiling JavaScript into opcode gets us part way there. It allows the client to translate the code into the machine’s native language more quickly. This field continues to evolve and is not yet widely used.No matter what approach is used, webapp developers must expire their code as far into the future as possible to allow for maximum caching.The JavaScript community and standards bodies need to continue working toward precompiled, cached machine code rather than interpreted JavaScript.

You might also like