Can WebAssembly make your web apps faster?
Tags: javascript, webassembly, c
Published: 2023-04-16 17:29:10

## What is WebAssembly?
### Assembly?
As we all know, machines read binary codes (strings of zeros and ones) because it is very convenient for them but not for humans. So engineers basically created a mapping from binary code and called the mapped version 'Assembly'.

![Assembly and binary code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o7lolx3sbg1oml9q1qof.png)

### WebAssembly? 
WebAssembly is an 'Assembly'-like language. It's nickname is _wasm_. Basically WebAssembly is the Assembly language for modern browsers. 

People usually don't write Assembly code directly because it can be tedious. That's why there are many Assembly compilers that simply create Assembly code from [C/C++](https://emscripten.org/), C#, Rust, Java ... So basically you can run an existing C/C++ or Rust application inside a browser.

WebAssembly code can be directly injected into Javascript or Node.js environment. This can be very useful because you can combine WebAssembly with Javascript.

WebAssembly provides near native performance. Javascript is just-in-time (JIT) compiled. But WebAssembly is pre-compiled. So that's why wasm can be executed faster than Javascript code. 

## Get your hands dirty
Firstly, all the source codes used in this post available in [GitHub](https://github.com/canbax/wasm0). The ultimate question in my mind is **"Can we sort an array of integers faster with WebAssembly?"**. Since we want to execute faster, I think I should use the holy programming language **C**. 
To compile WebAssembly, I used [emscripten](https://emscripten.org) I wrote C code and compiled C code into WebAssembly using emscripten.  I simply followed instructions at [MDN](https://developer.mozilla.org/en-US/docs/WebAssembly/C_to_wasm) and [emscripten](https://emscripten.org/docs/getting_started/downloads.html)

### Hello World!
After you download and install emscripten by following their documentation, you should have a `emsdk` folder. Go into that folder and run `source ./emsdk_env.sh`. Now you can compile your C codes into WebAssembly. To compile a hello world WebAssembly code, I used command `emcc -o hello3.html hello3.c --shell-file html_template/shell_minimal2.html -s NO_EXIT_RUNTIME=1 -s "EXPORTED_RUNTIME_METHODS=['ccall']"`. Here `emcc` is the emscripten compiler. `-o hello3.html hello3.c` means compile 'hello3.c' and output HTML file 'hello3.html'. My 'hello3.c' is like below

```
#include <stdio.h>
#include <emscripten/emscripten.h>

int main() {
    printf("Hello World\n");
    return 0;
}

#ifdef __cplusplus
#define EXTERN extern "C"
#else
#define EXTERN
#endif

EXTERN EMSCRIPTEN_KEEPALIVE void myFunction(int argc, char ** argv) {
    printf("MyFunction Called\n");
}
```
It simply prints 'Hello World' to the developer console when the web page is loaded. And also prints 'MyFunction Called' when the C function is called.

`--shell-file html_template/shell_minimal2.html` of the script basically uses a template html file to generate the output 'hello3.html' file. My 'shell_minimal2.html' is like below.

```
<html>
  <body>
    <button id="mybutton">Call the C function</button>
  </body>
</html>
{{{ SCRIPT }}}
<script>
  document.getElementById("mybutton").addEventListener("click", () => {
    alert("check console");
    const result = Module.ccall(
      "myFunction", // name of C function
      null, // return type
      null, // argument types
      null // arguments
    );
  });
</script>
```
The rest of the command is necessary to call the function using some special method called `ccal`. If you execute the command, it will generate 'hello3.html', 'hello3.js', and 'hello3.wasm' files. If you look at 'hello3.html' you will see
```
<html>
  <body>
    <button id="mybutton">Call the C function</button>
  </body>
</html>
<script async type="text/javascript" src="hello3.js"></script>
<script>
  document.getElementById("mybutton").addEventListener("click", () => {
    alert("check console");
    const result = Module.ccall(
      "myFunction", // name of C function
      null, // return type
      null, // argument types
      null // arguments
    );
  });
</script>
```
The only difference between the template file 'shell_minimal2.html' and 'hello3.html' is the `{{{ SCRIPT }}}` section. Inside the 'hello3.html', this part is replaced with `<script async type="text/javascript" src="hello3.js">` 'hello3.js' is a Javascript file that provides a global variable named `Module` and also imports `hello3.wasm`. `hello3.wasm` is a binary file. You cannot read it with naked eye easily. Start an HTTP server and open 'hello3.html' in a modern browser. I'm using VSCode for code editing and it has [a nice extension](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) for simple HTTP server. Then you can actually see that wasm file is converted to WebAssembly Text (.wat) format in your browser.
![WebAssembly Text representation in browser](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4m3yrmn5c3j6kfe5ri3w.png)
In developer console, you can see `printf` statements of C codes are actually printed. This is hello world for WebAssembly! Also if you click to the button, you will see it calls a C function! That's amazing! From JavaScript we can call a C function!
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kfajul8nvil6i9icmddr.png)

### Fibonacci
Now let's see if a C code can execute faster than plain Javascript code. To make a comparison I implemented fibonacci algorithm in a very inefficient way in both C and Javascript and executed side-by-side. 

I executed command just like hello world example `emcc -o hello4.html fib.c --shell-file html_template/simple_compare.html -sEXPORTED_FUNCTIONS=_fib -sEXPORTED_RUNTIME_METHODS=cwrap`. Here to call a C function, we use someother special method `cwrap`.
Below is my C code file 'fib.c'
```
int fib(int n)
{
    if (n < 2)
        return n;
    return fib(n - 1) + fib(n - 2);
}
```
Here is my 'simple_compare.html' file 
```
{{{ SCRIPT }}}
<input type="number" id="fibNum" value="35" />
<button id="mybutton">Compare JS vs C on Fibonacci</button>
<script>
  document.getElementById("mybutton").addEventListener("click", () => {
    const fibIndex = Number(document.getElementById("fibNum").value);
    const t1 = executeFibonacciOnC(fibIndex);
    const t2 = executeFibonacciOnJS(fibIndex);
    console.log("C time:", t1, "JS time:", t2);
  });

  // finds the 'n'th fibonacci number in C using the worst implementation and returns the execution time
  function executeFibonacciOnC(n) {
    fibC = Module.cwrap("fib", "number", ["number"]);
    const t1 = performance.now();
    const res = fibC(n);
    const t2 = performance.now();
    console.log("c result: ", res);
    return t2 - t1;
  }

  function executeFibonacciOnJS(n) {
    const t1 = performance.now();
    const res = fib(n);
    const t2 = performance.now();
    console.log("JS result: ", res);
    return t2 - t1;
  }

  function fib(n) {
    if (n < 2) return n;
    return fib(n - 1) + fib(n - 2);
  }
</script>

```
Here you can see that Javascript is a lot faster than C code. I feel like I wasted all my time and WebAssembly is just a balloon. 
![C vs JS on Fibonacci calculation without any compiler optimizations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/erqc5tbt7s7eqk6crf20.png)

Then I realized there is some compiler optimization flags in emscripten. Let's use them `emcc -o hello4.html fib.c --shell-file html_template/simple_compare.html -sEXPORTED_FUNCTIONS=_fib -sEXPORTED_RUNTIME_METHODS=cwrap -O2` I just added a `-O2` flag to my command and do it again.

Now you can see some magic!
![C vs JS on Fibonacci calculation with O2 compiler optimization flag](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bho7fjsubsgcjr2kgtiv.png)
This time, C code executes faster! In some cases it is like even 2 times faster!

### Sort numbers
Now let's try our ultimate aim. Can we sort numbers faster than Javascript? Let's try with C standard library function `qsort` I think it should be faster, C codes are usually fast. Similar to previous command, I executed command `emcc -o qsort.html arraySorter.c --shell-file html_template/simple_compare_array.html -sEXPORTED_FUNCTIONS=_arraySorter,_malloc,_free -sEXPORTED_RUNTIME_METHODS=cwrap` My 'arraySorter.c' is very simple like below. It simply calls `qsort` function with a comparer function.
```
#include <stdlib.h>

int compareFn(const void *a, const void *b)
{
    return (*(int *)a - *(int *)b);
}

void arraySorter(int *arr, int size)
{
    qsort(arr, size, sizeof(int), compareFn);
}
```

My template file 'simple_compare_array.html' is like below. Here passing and array as a parameter is bit hard. We need to use `malloc` and `free` functions to create and array and pass it to C.
```
{{{ SCRIPT }}}
<input type="number" id="arrSize" value="1000000" />
<button id="mybutton">Compare JS vs C on sorting integer array</button>
<script>
  function getArray() {
    const l = Number(document.getElementById("arrSize").value);
    return Array.from({ length: l }, () => Math.floor(Math.random() * l));
  }
  document.getElementById("mybutton").addEventListener("click", () => {
    const arr1 = getArray();
    const arr2 = Array.from(arr1);
    const t1 = arrayOperationsOnC(arr1);
    const t2 = arrayOperationsOnJS(arr2);
    console.log("C time:", t1, "JS time:", t2);
  });

  function arrayOperationsOnC(n) {
    const BYTE_SIZE_OF_INT = 4;
    const t1 = performance.now();
    const arraySize = n.length;
    const arrayPointer = Module._malloc(arraySize * BYTE_SIZE_OF_INT);
    Module.HEAP32.set(new Int32Array(n), arrayPointer / BYTE_SIZE_OF_INT);
    const cFunc = Module.cwrap("arraySorter", null, ["number", "number"]);
    cFunc(arrayPointer, arraySize);
    const resultArray = Array.from(
      Module.HEAP32.subarray(
        arrayPointer / BYTE_SIZE_OF_INT,
        arrayPointer / BYTE_SIZE_OF_INT + arraySize
      )
    );
    console.log(resultArray);
    Module._free(arrayPointer);
    const t2 = performance.now();
    return t2 - t1;
  }

  function arrayOperationsOnJS(n) {
    const t1 = performance.now();
    const res = arraySorter(n);
    console.log(res);
    const t2 = performance.now();
    return t2 - t1;
  }

  function arraySorter(arr) {
    return arr.sort((a, b) => a - b);
  }
</script>
```
If I execute this I see my C code is like 4 times slower! 
What the heck is wrong?

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z4ifpm66r66d216qrhb8.png)

OK I see I didn't use compiler optimization flags. Let's try `-O2` flag and do it again. Now it is faster but still very slower than Javascript.
> C time: 716.1 JS time: 269.5

Even if I use `-O3` flag, I see it is still slower. There is no '-O4' this is the most optimized.

> C time: 711.9 JS time: 270.6

Now we are sure that quick sort with C cannot pass plain Javascript. But we used C standard library function `qsort`. Can we try plain C code for quick sort? Let's try. I used command `emcc -o faster_sorter.html arraySorter2.c --shell-file html_template/simple_compare_array.html -sEXPORTED_FUNCTIONS=_arraySorter,_malloc,_free -sEXPORTED_RUNTIME_METHODS=cwrap -O2` Here I used the same HTML template but this time I implemented quick sort algorithm with plain C code. Below in my `arraySorter2.c` file.

```
// Quick sort in C

// function to swap elements
void swap(int *a, int *b)
{
    int t = *a;
    *a = *b;
    *b = t;
}

// function to find the partition position
int partition(int array[], int low, int high)
{

    // select the rightmost element as pivot
    int pivot = array[high];

    // pointer for greater element
    int i = (low - 1);

    // traverse each element of the array
    // compare them with the pivot
    for (int j = low; j < high; j++)
    {
        if (array[j] <= pivot)
        {

            // if element smaller than pivot is found
            // swap it with the greater element pointed by i
            i++;

            // swap element at i with element at j
            swap(&array[i], &array[j]);
        }
    }

    // swap the pivot element with the greater element at i
    swap(&array[i + 1], &array[high]);

    // return the partition point
    return (i + 1);
}

void quickSort(int array[], int low, int high)
{
    if (low < high)
    {

        // find the pivot element such that
        // elements smaller than pivot are on left of pivot
        // elements greater than pivot are on right of pivot
        int pi = partition(array, low, high);

        // recursive call on the left of pivot
        quickSort(array, low, pi - 1);

        // recursive call on the right of pivot
        quickSort(array, pi + 1, high);
    }
}

void arraySorter(int *arr, int size)
{
    quickSort(arr, 0, size - 1);
}
```
Now it seems with `-O2` flag we can sort integers faster than plain Javascript! Now C code is like 2 times faster. That's amazing!

> C time: 156.8 JS time: 271.5

Is Wasm faster in your browser? [Try and see](https://canbax.github.io/wasm0/) now. All the [source codes](https://github.com/canbax/wasm0) is available under MIT license.