WebAssembly, or WASM, is a new format that is portable, small, fast to load, and web-compatible

Introduction to the

WebAssembly is a new type of code that runs in modern Web browsers and provides new performance features and effects. It is designed not to write code by hand but to provide an efficient compilation target for low-level source languages such as C, C++, and Rust. For the front end, it allows the client APP to provide a form of code that runs multiple written languages on a network platform in close proximity to native performance.

For example, we write it in C ++, C, Rust, etc., compile it into WASM, throw it to the browser to run, and the browser will run it as a module. But its use is still relatively limited, for the front end, we use JavaScript to write is enough. However, we can use WebAssembly to improve performance in situations where we have very high performance requirements, such as games, complex computational logic behind drawing a complex canvas (which slows down the interface of drawing graphics) and so on.

The characteristics of

efficient

WebAssembly has a complete set of semantics. In fact, WebAssembly is a small, fast loading binary format whose goal is to maximize the capabilities of the hardware for native execution efficiency.

security

WebAssembly runs in a sandboxed execution environment and can even be implemented in an existing JavaScript virtual machine. In a Web environment, WebAssembly will strictly adhere to the same origin policy and browser security policy.

The development of

WebAssembly is a low-level language designed to have a very neat text format for debugging, testing, experimenting, optimizing, learning, teaching, or writing programs. You can view the source code for the WebAssembly module in this text format on a Web page.

standard

WebAssembly is designed to be versionless, feature-testable, and backward compatible on the Web. A WebAssembly can be called by JavaScript, in JavaScript context, and can invoke browser functionality just like a Web API. Of course, WebAssembly can run in a non-Web environment as well as in a browser.

Key Concepts of WebAssembly

To understand how WebAssembly works in a browser, you need a few concepts.

The module

Represents a WebAssembly binary that has been compiled into executable machine code by the browser. A module is stateless and like a binary object Blob can be cached in IndexDB or shared between Windows and Works (via the postMessage() function). A module can declare imports and exports just like an ES2015 module.

memory

ArrayBuffer, variable size. Essentially a contiguous array of bytes that can be read and written to by WebAssembly’s low-level memory access instructions.

form

Array with type, variable size. The entries in the table store references to objects that cannot be stored in memory as raw bytes, for security and portability reasons.

The instance

A module and all the states it uses at run time, including memory, tables, and a list of imported values. An example is like an ES2015 module that has been loaded with a specific set of global variables.

The JavaScriptAPI provides developers with the ability to create modules, memory, tables, and instances. Given a WebAssembly instance, JavaScript code can call code exposed by ordinary JavaScript functions. By importing JavaScript functions into a WebAssembly instance, any JavaScript function can be called synchronously by WebAssembly code.

Because JavaScript has complete control over how WebAssembly code is downloaded, compiled and run, JavaScript development can treat WebAssembly as a JavaScript feature that efficiently generates high-performance functions.

Underlying mechanisms and principles

As you can see from the browser execution flow above, WebAssembly code execution is shorter than JS code execution.

Let’s look at the execution process of THE JS code in V8 engine: The browser takes the source code, runs it through the Parse to generate the AST, and sends it to the Ignition interpreter to generate bytecode. If the same code is executed many times, it is marked as HotSpot code, and it is handed over to the TurboFan compiler, which compiles it into more efficient machine code and stores it. It is convenient to use machine code instead of bytecode to execute the code next time, improving the execution efficiency of the code.

The interpreter generates the AST, the compiler generates bytecode, machine code, optimizes, and then executes for GC garbage collection.

Wasm preempts some of the parsing and compilation to the development phase, whereas js parsing and compilation takes place at runtime, which slows down JS execution. However, JS GC can cause JS execution to lag, whereas WASM has no GC phase when executing in the browser. Its internal code is a language that supports manual manipulation of memory, so it can build garbage collector into its modules.

JavaScript API

WebAssembly.complier()

Promise<WebAssembly.Module> WebAssembly.complie(bufferSource)
Copy the code

As you can see from the prototype above, the complier() method returns a Promise object, so we can use the then method to retrieve the WASM content, but this data is the binary buffer of the Module, so we need to create this object for the class Module

WebAssembly.validate()
WebAssembly.validate(bufferSource)
Copy the code

This method is used to verify that the object received is correct and returns true/false

WebAssembly.instantiate()

Allows you to compile and instantiate WebAssembly code. This method has two overloads:

  • The first major way of reloading uses the typed array or ArrayBuffer form of WebAssembly binary code, which is compiled and instantiated together. The returned Promise carries the compiled Webassembly. Module and its first instantiated object, Webassembly.instance.

  • The second overloading uses compiled Webassembly. Module and returns a Promise that carries an instantiation object of the Module, Instance. The spectrum of the Module is quite useful if it’s already compiled or retrieved from cache.

class

WebAssembly.Module 

Contains no-load WebAssembly code that has been compiled by the browser and can be efficiently shared with Workers, cached in IndexDB, and instantiated multiple times.

The webassembly.module () constructor can be used to compile a given WebAssembly binary synchronously. However, the main way to get Module objects is through an asynchronous compilation function, such as WebAssembly.pile (), or by reading Module objects through IndexedDB.

WebAssembly.Instance

The Webassembly. Instance object itself is stateful and is an executable Instance of webassembly. Module. The instance contains all the WebAssembly export functions, allowing WebAssembly code to be called from JavaScript.

The webassembly.instance () constructor synchronously instantiates a Webassembly.module object. However, the usual way to get an instance is through the asynchronous function webassembly.instantiate ().

WebAssembly.Memory

The buffer property of this object is a resizable ArrayBuffer that stores the raw bytecode of the memory accessed by the WebAssembly instance.

Available for JavaScript and WebAssembly data sharing. JavaScript code is managed and executed in V8, while WASM is not. Although the WASM module is instantiated in V8, it only instantiates wASM as a whole. There is no way to probe execution within WASM, and WASM is generally written in a back-end language. They also have their own memory management. V8’s memory is provided by its upper-layer browser or Node, and V8 does not know what language the WASM module is written in, nor its memory.

V8 this category and wasm created instance inside this is equivalent to two processes, these two data exchange can be a function call returns a return value, but when want to exchange an object can be a problem, because their object format is different, wasm module in the back-end language may be some structure, it will has a problem. There needs to be a common memory space between them, and the specific data format is up to the developer.

WebAssembly.Table

The constructor creates a Table object based on the given size and element type.

This is a Javascript wrapper object wrapped around WebAssemble Table with a class array structure that stores multiple function references. Table objects created in Javascript or WebAssemble can be accessed and changed by Javascript or WebAssemble at the same time.

WebAssembly.CompikeError

The constructor creates a new WebAssembly CompileError object that represents errors during WebAssembly decoding or validation.

WebAssembly.RuntimeError

The constructor creates a new WebAssembly RuntimeError object – a type that will be thrown whenever a WebAssembly falls into the specified trap.

WebAssembly tool

AssemblyScript

Support for compiling Typescript directly into WebAssembly. This is a low barrier to entry for the front end.

Emscripten

It’s the soul of WebAssembly. Compile other high-level languages into WebAssembly.

WABT

A tool for converting WebAssembly between bytecode and text formats to make it easier for developers to understand what wASM is really doing. But the decompiled code is less than ideal.

Write WASM in C language

One of two ways

  1. Emscripten, which is relatively complex, requires configuration

  2. WasmFiddle, online

Emscripten

1. Clone the official project first, or directly download the project to the local, enter the project

# Get the emsdk repo
git clone https://github.com/emscripten-core/emsdk.git

# Enter that directory
cd emsdk
Copy the code

2. Run the following emsdk command to get the latest tools from GitHub and set them to Active (note the current directory)

# Download and install the latest SDK tools.
./emsdk install latest

# Make the "latest" SDK "active" for the current user. (writes .emscripten file)
./emsdk activate latest

# Activate PATH and other environment variables in the current terminal
source ./emsdk_env.sh
Copy the code

After this step, run the emcc command. If the following message is displayed, the installation is successful and no file is executed.

shared:INFO: (Emscripten: Running sanity checks)
emcc: error: no input files
Copy the code

Note: To perform EMCC, you must have GCC.

3. Create the C execution file

#include <stdio.h> include <stdio.h> Int main(int argc, char const *argv[]) {printf(" Hello WebAssembly! \n"); printf("%d\n", add(10, 20)); return 0; } // add function int add(int x, int y) {return x + y; }Copy the code

Then use GCC command to compile the file into executable C language file Hello to verify the correctness of the C language file.

gcc demo.c -o hello
./hello
Copy the code

4. Run the EMcc command to generate the file to be executed in the Node environment

emcc demo.c -o demo_node.js
node demo_node.js
Copy the code

This command generates two files, a JS file and a wASM file. You cannot execute the WASM file directly in Node. You need to bridge the generated JS file to execute it.

5. Run the EMCC command to generate the file to be executed in the browser environment

emcc demo.c -s WASM=1 -O3 -o  demo_html.html
Copy the code

This command generates three files, HTML, JS, and wASM. Note that the same origin policy needs to be addressed here, and can be started using http-server or live-server.

High performance computing

In the web development phase, JavaScript was originally designed as a single thread, and if you had multiple threads, Dom processing would be messy. One section of JavaScript would modify the Dom style, and one section of JavaScript would delete the Dom, which would be awkward. Therefore, JavaScript has always been based on single thread, but now due to the volume of business and some complex calculation will be very time-consuming, which leads to Dom rendering will lag, the user experience is very poor.

while(true){
  document.body.innerHTML += Math.random() + '<br>'
}
Copy the code

Concurrent.Thread.js

Concurrent.Thread.create(function() {
  while(true){
    document.body.innerHTML += Math.random() + '<br>'
  }
});
Copy the code

Worker

const worker = new Worker('task.js')
worker.onmessage = event => document.body.innerHTML += event.data + '<br>'
   
# task.js
while(true){
  postMessage(Math.random())
}
Copy the code

Atomics

<script> const worker = new Worker('task2.js'); const sharedArrayBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 1000000); const sharedArray = new Int32Array(sharedArrayBuffer); for (let i = 0; i < 10; i++) { Atomics.store(sharedArray, i, i + 1); } worker.postMessage(sharedArray); const itemValue = Atomics.load(sharedArray, 2); const result = 'joker' + itemValue; Atomics.store(sharedArray, result); const queuePos = 1; Atomics.notify(sharedArray, 2, queuePos); </script> <! -- task2.js --> self.addEventListener("message", e => { const shareArray = e.data; Atomics.wait(shareArray, 2, "joker"); The console. The log (' 🍉 '); })Copy the code

gpu.js

// GPU is a constructor and namespace for browser const gpu = new GPU(); const multiplyMatrix = gpu .createKernel(function (a, b) { let sum = 0; for (let i = 0; i < 512; i++) { sum += a[this.thread.y][i] * b[i][this.thread.x]; } return sum; }) .setOutput([512, 512]); Const c = multiplyMatrix(a, b); const c = multiplyMatrix(a, b); console.log(c);Copy the code

For details, see Github.

webAssembly

Write a C function that contains add and Square and compile it into a WASM file the same way as before, or you can use the online version.

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

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

Use JavaScriptAPI to load the C language

Function loadWebAssembly(path, imports={}) { return fetch(path) .then(res=>res.arrayBuffer()) .then(buffer => WebAssembly.compile(buffer)) .then(module=>{// Create WebAssembly Instance imports create space to create variable mappings etc. Return new WebAssembly.Instance(module, Function loadWebAssembly2(path, imports={}) { return fetch(path) .then(res=>res.arrayBuffer()) .then(WebAssembly.instantiate) .then(module=>module.instance) } loadWebAssembly('./math.wasm').then(instance => { console.log(instance); Const add = instance.exports. Add const squ = instance.exports. Square console.log(add(3,31)); console.log(squ(3)); })Copy the code

Other Usage Scenarios

  1. Game Business Scenarios

  2. 3D render scene

  3. Complex technology in the business

conclusion

Although the WebAssembly standard has been finalized and implemented by major browsers, the following issues remain:

  • Browser compatibility is not good, only the latest version of the browser support, and different browsers to JS WebAssembly interoperability API support is inconsistent;

  • The ecosystem tools are not perfect and mature. We can’t find a language to write WebAssembly smoothly at present. It’s still in its infancy.

  • Learning materials are too few, and many pits in the use process need to be stepped on.

In short, WebAssembly is not yet mature, and if your team doesn’t have intolerably high performance issues, it’s not the time to implement WebAssembly into your production, because it might interfere with your team’s productivity or block development with a pothole that can’t be easily resolved.

Write in the last

Share my possession of TS tutorials, from zero to high order all series, click a link, 0 RMB for www.yidengxuetang.com/pub-page/in…

Reference documentation

  1. Official Chinese website

  2. MDN WebAssembly

  3. Tencent Cloud WebAssembly Chinese document

  4. Compile WebAssembly files online