WebAssembly

On December 5, 2019, the World Wide Web Consortium (W3C) announced that the WebAssembly core specification is now an official Web standard, joining the family of Web standards after HTML, CSS, and JavaScript. “The advent of WebAssembly has expanded the range of applications that can be implemented with open Web platform technologies alone,” says Philippe LeHegaret, project leader at W3C. In today’s world of machine learning and artificial intelligence, it is important to run high-performance programs on the Web without compromising user security.”

So what exactly is WebAssembly, why was WebAssembly born, why does it achieve high performance, and why is it so fast? The following article to do a simple understanding of its understanding;

What is a WebAssembly

First, we need to know what WebAssembly really is; MDN is a fast, secure, portable low-level code format designed to run on modern processors (including browsers) for efficient execution and compact representation. It has a compact binary format and can run at near-native performance.

We can sum up as follows:

  • WebAssembly is not a language, but a new way of coding. It is another file format that can run in a browser, after HTML, CSS, and JavaScript.
  • Is a technical solution that allows you to write code in a non-javascript programming language.
  • Has a compact binary format that can run close to native performance

The background

When JavaScript was born in 1995, it wasn’t designed for fast execution (for simple web interactions (e.g., checking “username”), but not for complex applications.

In 2008, thanks to its rapid growth in the previous decade, browser manufacturers competed for browser performance with the introduction of the just-in-time compiler, or JIT; Jit-based modes allow JavaScript code to run faster and perform better

Then, with the improvement of performance, the application scope of JavaScript is greatly expanded, which gives birth to node. js, Electron and other technologies. The fundamental problems of JS are gradually exposed:

  • The syntax is too flexible to develop large Web projects
  • The performance cannot meet the requirements of some scenarios.

Major browser manufacturers have proposed their own solutions, such as:

  • Microsoft TypeScript improves the looser syntax of JS by adding static type checking to improve code robustness.
  • Google’s Dart introduces a new VIRTUAL machine for the browser to run the Dart application directly to improve performance.
  • Firefox asm.js is a subset of JS, and the JS engine is optimized for asm.js

However, these solutions have their own shortcomings and are incompatible, which goes against the purpose of the Web; This requires a unified technical specification scheme. So, WebAssembly was born;

In 2015, WebAssembly was first released and provided a small demo of a game running under Unity. The game runs directly in the browser;

On December 5, 2019, the World Wide Web Consortium (W3C) announced that the WebAssembly Core specification is an official Web standard;

As a result, WebAssembly is officially active in the Web field. After the introduction of JIT, WebAssembly is likely to be another turning point in the mechanism of running code in browsers.

Why Is WebAssembly faster?

The high performance of WebAssembly is relative to THAT of JS, so before we can have poor performance between JavaScript and WebAssembly, we need to understand what the JS engine does.

The most common way to translate code into machine language in current browsers is a JIT compiler. Here we take the JIT compiler as an example, roughly give a program startup steps and performance:

Each color bar in the figure represents a different task:

  • File fetching – The time it takes to fetch a file from the server
  • Parsing – the time it takes to turn source code into code that the interpreter can run;
  • Compile and optimize – represents the baseline compiler and the time spent optimizing the compiler.
  • Reoptimization phase – the time it takes the JIT to discard optimized code when it finds that the optimization assumptions are wrong. This includes the time to reoptimize, discard, and return to the baseline compiler.
  • Execution phase – the time when code is executed.
  • Garbage Collection phase – Garbage collection, time to clean up memory.

Let’s take a look at the time taken by WebAssembly and JS at various stages to compare their performance

WebAssembly VS JS

File fetching phase

Because WebAssembly can be binary, the JS equivalent of a WebAssembly file is smaller; This means that WebAssembly is faster than JavaScript at grabbing files. WebAssembly files are smaller than JavaScript even when JavaScript is compressed; This time difference is even more stark when the network speed is slow;

Parsing stage

When it reaches the browser, the JavaScript source code is parsed into an Abstract syntax tree (AST). The browser then adopts a lazy loading approach, parsing only the parts that are really needed, and leaving only piles of functions that the browser doesn’t need for the time being. The purpose of parsing is to convert the AST into intermediate code (called bytecode) that the JS engine can compile; However, WebAssembly does not need to be transformed because it is already bytecode. It just needs to be decoded and determined that there are no errors.

Compilation and optimization phases

Js is a weakly typed programming language. When initializing a variable, it is not necessary to explicitly indicate the specific type of the variable. The type of the variable is completely inferred by the code compiler during the operation of the code.

Strongly typed languages such as C language can carry out static compilation and optimization of program source code in advance, and finally directly generate the corresponding optimized binary machine code for CPU execution.

The converted WebAssembly is closer to the machine code, that is, WebAssembly is faster at this stage than JS because:

  • The compiler doesn’t have to spend time looking at the data types in the code as it runs, optimizing it at compile time
  • The compiler does not need to compile different versions of the same code.
  • Much of the optimization was done before LLVM, so compilation and optimization work is minimal.

To optimize

In some cases, the JIT will repeat the “discard optimized code <-> re-optimize” process.

This happens when the JIT makes assumptions during the optimization hypothesis phase that are found to be incorrect during the execution phase. For example, when a loop discovers that the variable type used in the current loop is different from the type used in the last loop, or when a new function is inserted in the prototype chain, the JIT will discard the optimized code.

The de-optimization process has two parts of overhead. First, it takes time to throw away the optimized code and go back to the baseline version. Second, if the function is still called frequently, the JIT may send it to the optimized compiler for another optimized compilation, which is a waste of time.

In WebAssembly, types are determined, so the JIT does not need to make optimization assumptions based on the type of a variable. That is, WebAssembly has no re-optimization phase.

perform

You can write efficient JavaScript code yourself. You need to understand JIT optimizations, such as what code compilers will treat it differently (discussed in the JIT article).

However, most developers are unaware of the JIT implementation mechanism. Even if a developer knows the mechanics of JIT, it can be difficult to write CODE that is JIT compliant because the coding patterns that people usually use to make code more readable are not the right ones for the compiler to optimize.

In addition, the JIT makes different optimizations for different browsers, so a good optimization for one browser is likely to perform poorly on another browser.

Because of this, WebAssembly execution is usually faster, and many of the optimizations that jits make for JavaScript are not needed in WebAssembly. In addition, WebAssembly is designed for compilers, so developers don’t program it directly, allowing WebAssembly to focus on providing more desirable instructions (ones that execute more efficiently) to the machine.

In terms of execution efficiency, different code functions have different effects, generally speaking, the execution efficiency can be improved by 10%-800%.

The garbage collection

In JavaScript, developers do not need to manually clean up unused variables in memory. The JS engine does this automatically, in a process called garbage collection.

However, when you want controllable performance, garbage collection can be a problem. The garbage collector will start automatically, which is out of your control, so chances are it will start at an inopportune time. Most browsers today already schedule garbage collection to start at a reasonable time, but this still adds overhead to code execution.

Currently, WebAssembly does not support garbage collection. Memory operations are manually controlled (like C and C++). This does increase development costs for developers, but it also makes the code more efficient to execute.

conclusion

With WebAssembly, you can run code faster on web applications. There are several reasons why WebAssembly code runs faster than JavaScript.

  • File loading – WebAssembly files are smaller, so they download faster.

  • Parsing – Decoding WebAssembly is faster than parsing JavaScript

  • Compilation and optimization – Compilation and optimization takes less time because more optimization is done before the file is pushed to the server, and JavaScript needs to compile the code multiple times for dynamic typing

  • Retuning – WebAssembly code does not need to be retuned because the compiler has enough information to get the correct code the first time it is run

  • Execution – Execution can be faster, and WebAssembly instructions are closer to machine code

  • Garbage Collection – Currently WebAssembly does not support garbage collection directly, and garbage collection is manually controlled, making it more efficient than automatic garbage collection.

Will WebAssembly replace JavaScript?

WebAssembly almost beats JavaScript at every stage. Will WebAssembly replace JS?

Personally, WebAssembly will not replace JS;

It is designed to work in concert with JS, as stated on the MDN website: “We can leverage the performance and power of WebAssembly and the expressive power and flexibility of JavaScript in the same application:

  • JS is dynamically typed, flexible and expressive, requires no compilation, and has a vast ecosystem of powerful frameworks, libraries, and other tools.
  • WebAssembly provides a way for code written in a variety of languages to run on the Web at near-native speeds
  • WebAssembly does not have DOM manipulation capabilities
  • JavaScript and WebAssembly can call each other.

Small instance of a WebAssembly application

Module generation:

We can compile a high-level language into a. Wasm file by installing a tool such as Emscripten.

git clone https://github.com/juj/emsdk.git/emsdk install --build=Release sdK-incoming-64bit binaryen-master-64bit./emsdk install --build=Release sdK-incoming-64bit binaryen-master-64bit activate --globalBuild =Release sdK-incoming-64bit binaryen-master-64bit # if you get the following error on your macOSError: No tool or SDK found by name 'sdk-incoming-64bit'/emsdk activate latest /emsdk activate latestCopy the code

Go to the emSDK folder and type the following command to take you through the process of compiling a sample C program into asm.js or wASM.

source ./emsdk_env.sh
Copy the code

Emscripten is powerful enough to generate HTML and JS templates directly, as well as specialized WASM files such as math.c to math.wasm

emcc math.c -Os -s WASM=1 -s SIDE_MODULE=1 -o math.wasm
Copy the code

At this point only math.wASM is generated

emcc hello.c -s WASM=1 -o hello.html
Copy the code

This command produces:

  • Hello. Wasm: Binary WASM module code
  • Hello.js: a JavaScript file that contains glue code for converting between native C functions and JavaScript/ wASM
  • Hello. HTML: an HTML file that loads, compiles, instantiates your WASM code, and outputs it to a browser display

Module is loaded

After the WASM module is created, we can obtain wASM resources and instantiate the module to complete the module loading

Obtain WASM resources

The resource is obtained in the same way that we normally get it, using XMLHttpRequest or Fetch. In this case, Fetch returns a Promise that can be parsed as a Response object. The response is then converted to a typed array using the arrayBuffer function, which returns a promise that can be parsed as a typed array.

Module instantiation

We get the WASM module and instantiate it using the Webassembly.instantiate function.

Javascript calls WebAssembly methods

math.c

int add (int x, int y) {
  return x + y;
}

int square (int x) {
  return x * x;
}
Copy the code

math.html

<! DOCTYPE html><html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <h1></h1>
    <script>
      / * * *@param {String} Path WASM file path *@param {Object} Imports are passed to the variable */ in the WASM code
      function loadWebAssembly(path, imports = {}) {
        return fetch(path) // Load the file
          .then((response) = > response.arrayBuffer()) / / to ArrayBuffer
          .then((buffer) = > WebAssembly.compile(buffer))
          .then((module) = > {
            imports.env = imports.env || {};

            // Open up memory space
            imports.env.memoryBase = imports.env.memoryBase || 0;
            if(! imports.env.memory) { imports.env.memory =new WebAssembly.Memory({ initial: 256 });
            }

            // Create a variable mapping table
            imports.env.tableBase = imports.env.tableBase || 0;
            if(! imports.env.table) {// In the MVP version element can only be "anyfunc"
              imports.env.table = new WebAssembly.Table({
                initial: 0.element: "anyfunc"}); }// Create a WebAssembly instance
            return new WebAssembly.Instance(module, imports);
          });
      }
      / / call
      loadWebAssembly("./math.wasm").then((instance) = > {
        const add = instance.exports.add; // get the method in c
        const square = instance.exports.square; // get the method in c

        console.log("10 + 20 =", add(10.20));
        console.log("3 * 3 =", square(3));
        console.log("(2 + 5) * * 2 =", square(add(2 + 5)));
      });
    </script>
  </body>
</html>

Copy the code

Effect:

We can see that the add and square methods in math.c are successfully referenced by JS;

WebAssembly basic introduction to MDN compiler C/C++ for WebAssembly use WebAssembly a few pictures let you understand WebAssembly front-end to understand WebAssembly prelife