1. What?
WebAssembly or wasm is a new portable, size- and load-time-efficient format suitable for compilation to the web.
A portable, small and fast-loading (binary) format, suitable for compiling to the Web
The main goal is to support high-performance applications in the Web environment. But the design does not rely on Web features, nor does it provide functions for Web features, and can also be used in other environments
A simple understanding is to define a compilation target format that can achieve near-native execution performance in any environment that supports this format. It is equivalent to allowing the expansion of native modules. In performance-critical scenarios, use other more suitable languages (such as C++) to implement, and then compile to WebAssembly in advance, you can get a performance experience comparable to native
The design goals are divided into 2 aspects:
Fast, safe and portable semantics
Fast: execute with performance close to native code and utilize functions common to all modern hardware
Security: The code is verified and executed in a memory-safe sandbox environment to prevent data corruption or security violations
Well-defined: Fully and precise definition of legal procedures and their behavior, in a way that is easy to infer informal and formal
Independent of hardware: Compile on all modern architectures, desktop or mobile devices, and embedded systems
Independent of language: no bias towards any specific language, programming model or object model
Independent of the platform: can be embedded in the browser, run as a stand-alone VM, or integrated into other environments
Open: Programs can interact with their environment in a simple and universal way
Efficient and portable representation
Small and exquisite: Binary format is smaller than typical text or native code format, and can be transmitted quickly
Modularity: The program can be split into smaller parts, which can be transmitted, cached and used separately
Efficient: It can be decoded, verified and compiled quickly in a single pass (traversal), which is equivalent to real-time (JIT) or ahead of time (AOT) compilation
Streaming: Allows to start decoding, verification and compilation as early as possible before all the data is available
Parallel: allows decoding, verification and compilation to be split into multiple independent parallel tasks
Portability: No assumptions about architectures that are not widely supported on modern hardware
Major browsers (Chrome, Edge, Firefox, and WebKit) jointly promote the standardization process:
WebAssembly is currently being designed as an open standard by a W3C Community Group that includes representatives from all major browsers.
PS This thing is led by the browser manufacturers (the four of them stand together to do things, it is worth looking forward to), just by the way, open standards (not only for the Web environment), the motivation comes from the desire to further improve the JS runtime performance, in V8 After the introduction of JIT, it is impossible to further improve performance because of the limitations of JS language features (such as interpreted and weak types). Web capabilities are getting stronger and stronger, client JS is getting heavier and heavier, and the need to further improve JS execution performance is still there, so there is WebAssembly's salary
2. Wasm and wast
We know that WebAssembly defines a binary format, this format is wasm, for example:
0061 736d 0100 0000 0187 8080 8000 0160
027f 7f01 7f03 8280 8080 0001 0004 8480
8080 0001 7000 0005 8380 8080 0001 0001
0681 8080 8000 0007 9080 8080 0002 066d
656d 6f72 7902 0003 6763 6400 000a ab80
8080 0001 a580 8080 0001 017f 0240 2000
450d 0003 4020 0120 0022 026f 2100 2002
2101 2000 0d00 0b20 020f 0b20 010b
The C code corresponding to this string of hexadecimal numbers is:
// 辗转相除法求最大公约数
int gcd(int m, int n) {
if (m == 0) return n;
return gcd(n % m, m);
}
The readability of wasm is equal to 0. In order to alleviate this problem, a text format with better readability is defined, called wast:
(module
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "gcd" (func $gcd))
(func $gcd (; 0 ;) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(block $label$0
(br_if $label$0
(i32.eqz
(get_local $0)
)
)
(loop $label$1
(set_local $0
(i32.rem_s
(get_local $1)
(tee_local $2
(get_local $0)
)
)
)
(set_local $1
(get_local $2)
)
(br_if $label$1
(get_local $0)
)
)
(return
(get_local $2)
)
)
(get_local $1)
)
)
The parentheses are a bit Lisp-style, but at least they are readable, for example:
// 导出了两个东西,分别叫`memory`和`gcd`
(export "memory" (memory $0))
(export "gcd" (func $gcd))
// 函数签名,接受2个int32类型参数,返回int32类型值
(func $gcd (; 0 ;) (param $0 i32) (param $1 i32) (result i32)
// 函数体...就不猜了
PSwast and wasm can be converted to each other, see WABT: The WebAssembly Binary Toolkit for details
In addition, you can see another text command in the Source panel of the browser:
func (param i32 i32) (result i32)
(local i32)
block
get_local 0
i32.eqz
br_if 0
loop
get_local 1
get_local 0
tee_local 2
i32.rem_s
set_local 0
get_local 2
set_local 1
get_local 0
br_if 0
end
get_local 2
return
end
get_local 1
end
It looks very similar to wast, don’t know if there is a name, or it also belongs to wast? This is converted by the browser based on wasm
3. Trial play environment
environmental requirements:
C/C++ compilation environment Emscripten
A browser that supports WebAssembly (the latest Chrome supports it by default)
Does the online environment hurt? Demo environment: WebAssembly Explorer
COMPILE and DOWNLOAD can get wasm, it's easy to use
Note that the default is the C++ environment. If you want to use C, select C99 or C89 on the left, otherwise the function name will be corrupted, such as the wast of C++11:
(module
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "_Z3gcdii" (func $_Z3gcdii))
(func $_Z3gcdii (; 0 ;) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(block $label$0
(br_if $label$0
(i32.eqz
(get_local $0)
)
)
(loop $label$1
(set_local $0
(i32.rem_s
(get_local $1)
(tee_local $2
(get_local $0)
)
)
)
(set_local $1
(get_local $2)
)
(br_if $label$1
(get_local $0)
)
)
(return
(get_local $2)
)
)
(get_local $1)
)
)
The function name has been compiled into _Z3gcdii. It is guessed that something like namespace is causing trouble. I am not familiar with C++. Use C obediently.
PS In addition to C/C++, other languages can also play WebAssembly, such as Rust
Download platform SDK for local environment
Follow the installation steps
If nothing else, you can install it here, you can try emcc -v:
INFO:root:(Emscripten: Running sanity checks)
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.37.22
clang version 4.0.0 (emscripten 1.37.22 : 1.37.22)
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: D:\emsdk-portable-64bit\clang\e1.37.22_64bit
INFO:root:(Emscripten: Running sanity checks)
You may encounter a DLL missing (MSVCP140.dll) error in the Windows environment. You can manually install the required C++ environment. For details, see MSVCP140.dll not found · Issue #5605 · kripken/emscripten
Then you can make a try (save the previous C code as a file gcd.c):
emcc ./c/gcd.c -Os -s WASM=1 -s SIDE_MODULE=1 -s BINARYEN_ASYNC_COMPILATION=0 -o ./output/gcd.wasm
PS For more usage, see Emscripten Tutorial
The content of gcd.wasm is as follows:
0061 736d 0100 0000 000c 0664 796c 696e
6b80 80c0 0200 010a 0260 027f 7f01 7f60
0000 0241 0403 656e 760a 6d65 6d6f 7279
4261 7365 037f 0003 656e 7606 6d65 6d6f
7279 0200 8002 0365 6e76 0574 6162 6c65
0170 0000 0365 6e76 0974 6162 6c65 4261
7365 037f 0003 0403 0001 0106 0b02 7f01
4100 0b7f 0141 000b 072b 0312 5f5f 706f
7374 5f69 6e73 7461 6e74 6961 7465 0002
0b72 756e 506f 7374 5365 7473 0001 045f
6763 6400 0009 0100 0a40 0327 0101 7f20
0004 4003 4020 0120 006f 2202 0440 2000
2101 2002 2100 0c01 0b0b 0520 0121 000b
2000 0b03 0001 0b12 0023 0024 0223 0241
8080 c002 6a24 0310 010b
Note that the method name will be prefixed with an underscore (_) by default. In this example, the exported method name is _gcd. For details, see Interacting with code:
The keys passed into mergeInto generate functions that are prefixed by _. In other words myfunc: function() {}, becomes function _myfunc() {}, as all C methods in emscripten have a _ prefix. Keys starting with $ have the $ stripped and no underscore added.
The module interface used in JS should be underlined (I don’t know if there is a configuration item that can remove it)
Four. Try it
WebAssembly.compile(new Uint8Array(`
0061 736d 0100 0000 0187 8080 8000 0160
027f 7f01 7f03 8280 8080 0001 0004 8480
8080 0001 7000 0005 8380 8080 0001 0001
0681 8080 8000 0007 9080 8080 0002 066d
656d 6f72 7902 0003 6763 6400 000a ab80
8080 0001 a580 8080 0001 017f 0240 2000
450d 0003 4020 0120 0022 026f 2100 2002
2101 2000 0d00 0b20 020f 0b20 010b
`.match(/\S{2}/g).map(s => parseInt(s, 16))
)).then(module => {
const instance = new WebAssembly.Instance(module);
console.log(instance.exports);
const { gcd } = instance.exports;
console.log('gcd(328, 648)', gcd(328, 648));
});
The hexadecimal string comes from the online demo, which is consistent with the original wasm example. Just stick these things to Chrome's Console for execution. If everything is normal, you will get an error:
VM40:1 Uncaught (in promise) CompileError: WasmCompile: Wasm code generation disallowed in this context
This is because the default CSP (Content Security Policy) restrictions are easy to solve, just turn on the incognito mode (Ctrl/CMD + Shift + N)
Will get the output:
{memory: Memory, gcd: ƒ}
gcd(328, 648) 8
The first line is the export content of the module obtained by loading our WebAssembly, including a memory object and gcd method, and the output of the second line is the greatest common divisor calculated by calling the high-performance module
WebAssembly.compile and other related APIs can refer to:
JavaScript API-WebAssembly: Specification definition
WebAssembly-JavaScript | MDN: contains examples
In addition, the locally compiled version requires imports env (and the function name is prefixed with an underscore _):
WebAssembly.compile(new Uint8Array(`
0061 736d 0100 0000 000c 0664 796c 696e
6b80 80c0 0200 010a 0260 027f 7f01 7f60
0000 0241 0403 656e 760a 6d65 6d6f 7279
4261 7365 037f 0003 656e 7606 6d65 6d6f
7279 0200 8002 0365 6e76 0574 6162 6c65
0170 0000 0365 6e76 0974 6162 6c65 4261
7365 037f 0003 0403 0001 0106 0b02 7f01
4100 0b7f 0141 000b 072b 0312 5f5f 706f
7374 5f69 6e73 7461 6e74 6961 7465 0002
0b72 756e 506f 7374 5365 7473 0001 045f
6763 6400 0009 0100 0a40 0327 0101 7f20
0004 4003 4020 0120 006f 2202 0440 2000
2101 2002 2100 0c01 0b0b 0520 0121 000b
2000 0b03 0001 0b12 0023 0024 0223 0241
8080 c002 6a24 0310 010b
`.match(/\S{2}/g).map(s => parseInt(s, 16))
)).then(module => {
let imports = {
env: {
memoryBase: 0,
memory: new WebAssembly.Memory({ initial: 256 }),
tableBase: 0,
table: new WebAssembly.Table({ initial: 0, element: 'anyfunc' })
}
};
const instance = new WebAssembly.Instance(module, imports);
console.log(instance.exports);
// 注意下划线前缀
const { _gcd } = instance.exports;
console.log('gcd(328, 648)', _gcd(328, 648));
});
You can get similar output:
{__post_instantiate: ƒ, runPostSets: ƒ, _gcd: ƒ}
gcd(328, 648) 8
It should be that Emscripten has added some irrelevant things by default, which are functionally equivalent to our simplified version
V.
Advantages, disadvantages and application scenarios The
code size is small
About 300k (compressed) JavaScript logic is rewritten with WebAssembly, the volume is only about 90k
But using WebAssembly requires the introduction of a 50k-100k JavaScript class library as the infrastructure
Security is slightly improved
Although the WebAssembly text instructions corresponding to the source code are still unconcealed, the reverse cost is higher
Performance improvement
In theory, WebAssembly has close to native execution performance, because it skips the interpretation process, and the file size also has advantages in terms of transmission.
Of course, the premise is that in scenarios where the amount of business code is large and extreme performance is required, in repeated execution scenarios such as benchmarks, JIT is not much slower than AOT.
Disadvantages
currently limited capacity:
Only a few basic data types are supported (i32 / i64 / f32 / f64 / i8 / i16)
No direct access to DOM and other Web APIs
Unable to control GC
Application Scenario
WebAssembly defines a standard executable binary format for browsers, so that more developers can participate through a unified compilation mechanism to build a prosperous Web ecosystem. The vision is good, but there are some practical problems.
First of all, the original intention of WebAssembly is to "support high-performance applications in the Web environment". In order to break through the performance bottleneck, the possible application scenarios are:
Video decoding
Image Processing
3D/WebVR/AR visualization
Rendering engine
Physics engine
Compression/encryption algorithm
…Etc. scenes with a lot of computation
Of course, some support may also be built into the browser in the future, instead of using "extensions" or the like. But the real meaning of WebAssembly is to provide the ability to allow self-extension of high-performance "native" modules. After all, it may take a long time to wait for the browser to provide it and wait until the compatibility is acceptable. With this ability, No need to wait for the mainstream browsers on the market to support a certain native feature, you can do it yourself, and there is no compatibility difference. Conversely, a batch of popular community modules may emerge and gradually be adopted as browser native support, ecologically giving back to the Web environment
Reference
WebAssembly
WebAssembly in practice: How to write code: a great guide
How to comment on the latest WebAssembly bytecode technology in browsers?
WebAssembly: The silver bullet to solve JavaScript's chronic problems?
WebAssembly, the new era of the Web
Can WebAssembly be polyfilled?
wasm-arrays: WebAssembly array packaging library