JavaScript
Article
By Afshin Mehrabani

Understanding asm.js

By Afshin Mehrabani

asm.js is currently a trendy subject in web development. Reading a complete guide about asm.js, its goals, and its roadmap is impossible because you’d have to read multiple articles and put them together yourself. This article attempts to wrap up almost everything you need to know about asm.js using a step-by-step guide and real world examples, plus a couple of benchmarks.

History

JavaScript is one of the world’s most popular programming languages. You can use it in web browsers as a client-side language. Now, with the advent of NodeJS, JavaScript is also a popular language for server-side applications. Back in the day (actually, to this day), transpilers (source-to-source compilers) were used to hide some of JavaScript’s ugly parts. CoffeeScript, ClojureScript, and TypeScript are some of the more popular transpilers.

Transpilers mainly use an existing language (such as C or C++) or, they define a new language (like CoffeeScript). Then, instead of writing JavaScript, you can develop in this other language that the transpiler converts to JavaScript. In this article, we are going to review Emscripten, an LLVM bytecode to JavaScript transpiler.

So, What is asm.js?

asm.js is a strict subset of JavaScript. It is not a new language. asm.js is a restricted set of definitions that provide good performance characteristics. These definitions can be combined, like assembly language instructions, to create very fast JavaScript applications. asm.js takes advantage of some low level JavaScript features like Typed Arrays. It does not use any plugins or modules to run the JavaScript code, making it backward compatible.

How it Works

The main idea is generally about using JavaScript more strictly. For instance, eliminating the dynamic types. In order to provide an example, we are going to declare a variable and assign an integer value to it. Then, we declare another variable and assign the previous variable to the new one. Below, you will find the example in standard JavaScript.

var first = 5;
var second = first;

The corresponding asm.js syntax for the code presented above is as follows:

var first = 5;
//By using a bitwise operator, we make sure that the value is 32-bit integer
var second = first | 0;

The only difference between the first and the second code samples is the bitwise OR operator on the last line of the asm.js example. By using the bitwise operator we convert the value of the first variable to a 32-bit integer. This ensures that second is always treated as a 32-bit integer. asm.js has a number of other similar rules. By combining these rules with normal JavaScript, much faster code can be created. For more information about these rules and how they work please refer to the asm.js specification.

It is worth mentioning that it is not a good idea to write asm.js code by hand. The result would be hard to maintain and time consuming to debug. Based on this observation the question that remains is – how can we develop apps using asm.js?

The good news is that a few tools exist for generating JavaScript code according to the asm.js specification from other languages like C or C++. We will focus on Emscripten in this article, but keep in mind that a number of similar tools exist.

So, what is Emscripten? The answer is that it is an LLVM-to-JavaScript compiler. Emscripten accepts LLVM bytecode and converts them to asm.js JavaScript. So, how do you generate LLVM bytecode? You can use Clang to convert C/C++ codes to LLVM! In order to understand this process better, please consider the following diagram:

CPP to JS Workflow

Here are the steps to generate JavaScript code using Emscripten:
1. Create a C/C++ app.
2. Compile it using Clang to generate LLVM bytecode.
3. Pass the bytecode to Emscripten to get the JavaScript code.

It is worth mentioning that Emscripten performs the last two steps itself. Thus, the only thing that you need to do is pass the C/C++ code to Emscripten and get the JavaScript output.

--ADVERTISEMENT--

Hello World

Let’s write a simple program in C++ and convert it to an asm.js-based JavaScript application. The following, is a C++ code snippet that calculates the Fibonacci number for n=45.

#include <stdio.h>

int fib(int x) {
  if (x < 2) {
    return 1;
  } else {
    return fib(x - 1) + fib(x - 2);
  }
}

int main() {
  int result = fib(45);

  printf("%d\n", result);
  return 1;
}

As you can see, the algorithm is pretty simple and straightforward. To convert this C++ code to JavaScript, you need to install Emscripten first. Refer to the Emscripten wiki for installation instructions. After installing Emscripten, you can simply convert the C++ code by using the following command.

./emcc -O1 -s ASM_JS=1 ./hello_world.cpp

By setting the ASM_JS property to 1, you force Emscripten to emit JavaScript code based on the asm.js specification. After issuing the command, you will get an a.out.js file. This is a JavaScript file which is created from your C++ code. In order to execute the corresponding JavaScript file, you can install Node.js and issue the following command.

node ./a.out.js

By executing the above command you will see the output of the application. Congratulations, you made your first asm.js app.

Let’s See a Benchmark

In this section, we provide a comparison of the execution time of the native JavaScript code for the Fibonacci example presented above with the asm.js-based version. To have a more suitable picture for this comparison, we compile the C++ code using the Clang compiler and execute the corresponding generated native code. Therefore, we can compare three different environments – (1) normal JavaScript, (2) asm.js code, and (3) native application. To compile the C++ code using clang, simply issue the following command:

clang ./hello_world.cpp

This will create an a.out file containing your native executable.

The normal JavaScript example is shown in the following code.

var result = 0;

function fib(x) {
  if (x < 2) {
    return 1;
  } else {
    return fib(x - 1) + fib(x - 2);
  }
}

result = fib(45);
console.log(result);

We can run the normal JavaScript and asm.js examples using the following commands.

node ./handmade.js
node ./a.out.js

To execute the native app, run the following command.

./a.out

The resulting execution times are shown in the following figure. The experiments were performed on an OS X 10.9.2 operating system leveraging the latest version of Clang, Node.js, and Emscripten.

JS vs. asm.js vs. Native Performance

Conclusion

In this article, we talked about asm.js, one of the Mozilla’s specifications to create a low-level JavaScript environment. asm.js is a faster subset of the JavaScript programming language. We studied Emscripten, which is a tool to convert C/C++ code to LLVM bytecode and then to asm.js JavaScript code. We also created a very simple “Hello world” program using C++ code which we converted to asm.js using Emscripten. Furthermore, we provided some benchmarks in order to demonstrate the considerable difference in performance between the regular and asm.js-based JavaScript codes. It should be noted that asm.js is still a work in progress, and the performance gains should get even better in the future.

Recommended
Sponsors
The most important and interesting stories in tech. Straight to your inbox, daily. Get Versioning.
Login or Create Account to Comment
Login Create Account