Billions of bits of JS performance history
In 1995, javaScript was born and grew rapidly. In 2008, the introduction of the browser’s JITs just-in-time compiler made JS execution 10 times faster. There are more and more designs that can be added to the site. As more frameworks and tools are created, the ceiling for front-end projects gets higher and higher.
So if I want to realize video editing, image processing, VR and other computationally-heavy operations in the front end, is it feasible? The answer is yes, of course, but at the current speed of JS computing, your client will probably leave the page before you finish loading the computation when performing complex tasks. What about 3D games? Yes, yes, if the customer can accept a POWERPOINT game experience.
WebAssembly(WASM) was created to solve this problem.
Faster computing -WebAssembly
According to MDN, WebAssembly is a new way of encoding that can run in modern Web browsers – it is a low-level assembler like language with a compact binary format that can run close to native performance and provides a compilation target for languages such as C/C ++, So that they can run on the Web. It is also designed to coexist with JavaScript, allowing the two to work together.
We know that high-level languages are cheaper to learn because they are closer to human languages, but not as efficient to run. High-level languages are converted into low-level languages by a series of operations such as compilation — binary languages that machines can understand. As a language moves closer to a machine language, the various transitions can be omitted and become faster. You can think of it this way, first there were machine languages, but then there were high-level languages for people to develop and use, and in order for machines to understand high-level languages, high-level languages need to be transformed.
WebAssembly is a low-level assembly-like language whose binary format and near-machine code implementation make it much faster than javaScript to perform the same operations. Some compiler tools provide C/C++ to convert to wASM, and you can run these files on the Web. They usually end with.wasm. If you go to some sites and open debugging tools, you can find these files in the Network bar. But I didn’t know what they were.
According to the current needs of our company, face recognition via user image is one of our important functions. Such functions can use the face recognition library originally developed by C++, as long as the library is compiled into WebAssembly and open for use. This leaves the work of face recognition to WASM, and even in some non-DOM scenarios we can calculate with the worker without using the main thread. In this way, WASM combined with JS can help us implement some performance consuming operations more quickly.
In summary, WebAssembly can perform tasks much faster because it eliminates a lot of conversion from a high-level language to a low-level language and does not require additional type optimization and garbage collection for WASM.
Wasm User Guide ð§
The import
WebAssembly has not been integrated with the
This technology is being developed and in the future WebAssembly modules will be able to be loaded using
Currently we can extract network resources by using FETCH.
WebAssembly.instantiateStreaming(fetch('simple.wasm'), importObject)
.then(results= > {
// Do something with the results!
});
Copy the code
Note that the instantiateStreaming method is not supported on Safari, we can change to instantiate for compatibility with more browsers, the only difference is that we need to perform an extra step to convert the retrieved bytecode to ArrayBuffer.
fetch('module.wasm').then(response= >
response.arrayBuffer()
).then(bytes= >
WebAssembly.instantiate(bytes, importObject)
).then(results= > {
// Do something with the results!
});
Copy the code
Instantiate returns wASM Module and Instance. What are Module and Instance things? Let’s look at some of the key concepts in the WASM apis.
Relevant concepts
- Module: Represents a WebAssembly binary that has been compiled by the browser into executable machine code. Module is stateless, therefore, like
Blob
Again, you can pass between Windows and workerpostMessage()
) to be Shared. Module declarations import and export just like ES2015module. - Memory: A resizable ArrayBuffer that contains linear arrays of bytes read and written by WebAssembly’s low-level Memory access instructions.
- Table: a resizable array of typed references (for example, functions).
- Instance: a module that pairs with all states used at runtime, including
Memory
.Table
And a set of imported values (importObject
).
The Module with the instance
Module can be shared with worker on Windows, so that we can hand the appropriate work to worker, who instantiates and returns the result to Windows.
// index.js
var worker = new Worker("wasm_worker.js");
WebAssembly.compileStreaming(fetch('simple.wasm'))
.then(mod= >
worker.postMessage(mod)
);
Copy the code
CompileStreaming is similar to instantiateStreaming except that compileStreaming does not return instances, only compilations that return a Module. Next, we pass the Module to the worker.
CompileStreaming also has Safari compatibility issues and can be replaced with compile with the Instantiate method which requires an extra step to convert the retrieved bytecode to ArrayBuffer.
Simple. Wasm:
(module (func $imports.imported_func (; 0;) (import "imports" "imported_func") (param i32)) (func $exported_func (; 1) (export "exported_func") i32.const 42 call $imports.imported_func ) )Copy the code
Wasm_worker. Js:
var importObject = {
imports: {
imported_func: function(arg) {
console.log(arg); }}}; onmessage =function(e) {
console.log('module received from main thread');
var mod = e.data;
WebAssembly.instantiate(mod, importObject).then(function(instance) {
instance.exports.exported_func();
});
var exports = WebAssembly.Module.exports(mod);
console.log(exports[0]);
};
Copy the code
The worker receives the instance and instantiates it using Instantiate to get the instance. Exported_func is one of wASM’s exported_func function methods, we just need to call wASM exported function like js function. . Besides we can by the static method WebAssembly Module. Exports to obtain wasm export array of objects. In the example above, export exports the following array of objects
[{"name": "[exported_func](url)"."kind": "function"}]Copy the code
WebAssembly. Module. Imports can obtain import of wasm accept an array of objects, in the example above, the import into an array of objects are as follows
[{"module": "imports"."name": "imported_func"."kind": "function"}]Copy the code
By looking at the array of imported objects, we can write the javaScript function object importObject that contains the wASM executable and pass in the instantiation function. In the example above, wASM’s exported_func method invokes importObject’s imported_func method, printing 42 constants in WASM.
Try using the debugging Module with the worker example to better understand the above example.
memory
In wASM collaboration with javaScript, we can use Webassembly.memory to create Memory instances to pass information between the two.
var memory = new WebAssembly.Memory({initial:10.maximum:100});
Copy the code
The above code creates a memory space that starts with 10 and can be up to 100 for WASM data storage. Memory.buffer returns the raw binary data buffer ArrayBuffer. If we need to assign a value to memory, according to MDN, we cannot directly manipulate the contents of the ArrayBuffer, but we can create one of the typed array objects or use DataView.
var i32 = new Uint32Array(memory.buffer);
for (var i = 0; i < 10; i++) {
i32[i] = i;
}
Copy the code
In this way, we assign 0-9 to a 32-bit array of unsigned integers.
Here is a wASM file that provides the ability to add values from arrays:
(module (memory $js.mem (; 0;) (import "js" "mem") 1) (func $accumulate (; 0;) (export "accumulate") (param $var0 i32) (param $var1 i32) (result i32) (local $var2 i32) (local $var3 i32) local.get $var0 local.get $var1 i32.const 4 i32.mul i32.add local.set $var2 block $label0 loop $label1 local.get $var0 local.get $var2 i32.eq br_if $label0 local.get $var3 local.get $var0 i32.load i32.add local.set $var3 local.get $var0 i32.const 4 i32.add local.set $var0 br $label1 end $label1 end $label0 local.get $var3 ) )Copy the code
In WebAssembly’s low-level memory model, memory is represented as a contiguous untyped range of bytes, called linear memory. Here is a brief introduction to the calculation method in memory. We select a section of WASM for a simple explanation
local.get $var0
local.get $var1
i32.const 4
i32.mul
i32.add
local.set $var2
Copy the code
- $var0 pushed to the top of the stack
- $var1 pushed to the top of the stack
- 4 Push to the top of the stack
- Push the top of the stack twice, multiply the data obtained twice and push the result to the top of the stack
- Push the top of the stack twice, add the data obtained twice, and push the result to the top of the stack
In the wASM file, $accumulate takes two parameters, which can be thought of as the start and end of the array to be added. The local variable $var2 evaluates to the array boundary, incrementing by 4 on each loop.
Here I guess that with WASM, the array is passed as a group of 8-bit binaries, increment by 4 each time through the loop, which is exactlyUint32Array
The length of a data in.
In short, we can understand that in each loop, values in Uint32Array are read in sequence and added, and finally assigned to $VAR3 and output the result, that is, the sum of 0-9 bits of values in Uint32Array.
The complete js call looks like this:
var memory = new WebAssembly.Memory({initial:10.maximum:100});
WebAssembly.instantiateStreaming(fetch('memory.wasm'), { js: { mem: memory } })
.then(obj= > {
var i32 = new Uint32Array(memory.buffer);
for (var i = 0; i < 10; i++) {
i32[i] = i;
}
var sum = obj.instance.exports.accumulate(0.10);
console.log(sum);
});
Copy the code
Try debugging the memory instance to better understand the above example.
Table
The following WASM shows that wASM exports a table containing two elements: $thirteen and $fourtytwo methods.
(module (func $thirteen (result i32) (i32.const 13)) (func $fourtytwo (result i32) (i32.const 42)) (table (export "tbl") anyfunc (elem $thirteen $fourtytwo)) )Copy the code
Next comes the js part (omitting the import step) :
var tbl = results.instance.exports.tbl;
console.log(tbl.get(0) ());/ / 13
console.log(tbl.get(1) ());/ / 42
Copy the code
Notice that each function reference is retrieved through the table.prototype.get () call, and then an extra parenthesis is added at the end to call the function.
Try debugging the table instance to better understand the above example.
conclusion
- WebAssembly provides a way to run over the Web at near-native speed, and javaScript gives the Web a huge performance boost
- It can be used for video editing, image editing, 3D games, VR and other scenarios that require significant performance improvements
- WebAssembly is not written purely by hand, but is intended to be an effective compilation target for C, C ++, Rust, and other source languages
- Wasm instance /memory/table exchange information, to achieve the purpose of running wASM related code and get results.