preface
- Goal: Get a hands-on understanding of the WebAssembly development process and a simple performance comparison
- Example: Implement the Leetcode – Fibonacci sequence
- Preparation: The bash command line is based on the Mac environment, so some Windows students may need to replace it
- Study duration: about 30 minutes
- Packaging tool: WebPack 5
- Writing time: 2021-10-13
- Github: dive-into-wasm
1. Environment installation
Install Rust and the corresponding packages.
1.1 installation Rust
$ curl -sSf https://static.rust-lang.org/rustup.sh | sh
Copy the code
1.2 Replace it with domestic source
Change to domestic source, otherwise the installation is too slow.
Replace-with pings from all domestic sources in the replace-with/cargo/config file: ~/.cargo/config
[source. Crates - IO] registry = "https://github.com/rust-lang/crates.io-index" # replace with your preferred image source replace-with = 'sjtu' # Tsinghua University [source. Tuna] registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git" # [source. Ustc] university of science and technology of China Registry = "git://mirrors.ustc.edu.cn/crates.io-index" # of Shanghai jiaotong university registry = [source. Sjtu] "Https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index" # rustcc community [source rustcc] registry = "git://crates.rustcc.cn/crates.io-index"Copy the code
1.3 installationcargo-generate
The scaffold
$ cargo install cargo-generate
Copy the code
1.4 installationwasm-pack
Wasm-pack compiles Rust to WebAssembly.
$ cargo install wasm-pack
Copy the code
Complete a Rust Lib project
2.1 Creating the Rust Project
$ cargo new rust --lib
Copy the code
Here rust is the name of the project, and you can change it to anything you want.
Note: a folder named rust is automatically generated. Do not create this folder manually.
2.2 configurationCargo.toml
Add wASM-bindgen dependencies to [dependencies]. Add wASM-bindgen dependencies to [dependencies].
[package] name = "rust" version = "0.1.0" edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html/lib crate -type = [" cdylib "] [dependencies] wasm - bindgen = "0.2"Copy the code
2.3 lib.rs
Code implementation
The code details are omitted here, but the file contents are as follows:
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn fib(i: u32) - >u32 {
match i {
0= >0.1= >1,
_ => fib(i - 1) + fib(i - 2)}}#[wasm_bindgen]
pub fn fib_tail_call_optimized(i: u32, prev: u32, next: u32) - >u32 {
match i {
0 => next,
1 => next,
_ => fib_tail_call_optimized(i - 1, next, prev + next)
}
}
Copy the code
2.4 compiled toWebAssemblybinary
In the root directory of the Rust project, compile with the wASM-pack tool:
$ wasm-pack build
Copy the code
On success, something like the following will be printed:
. [INFO]: 📦 Your wASM PKG is ready to publish at... /dive-into-wasm/rust/pkg.Copy the code
In this case, the following five files are generated in the PKG directory: rust.js, rust.d.ts, rust_bg.js, rust_bg.wasm, and rust_bg.wASM.d. ts. Rust_bg.wasm is the binary file, and that’s what we’ll be using later.
Finish a front-end project
Complete a front-end project and compile to the browser to execute the generated WebAssembly.
3.1 Creating a front-end project
Execute in the parent directory of the rust directory (that is, the Web directory and the rust directory are horizontal, otherwise the path in the sample code for this tutorial would have to be adjusted) :
$ mkdir web && cd web
$ npm init -y
Copy the code
3.2 Configuring the WebPack and development Server
Install dependencies:
$ npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin
Copy the code
Webpack 5 does not support WebAssembly by default, so you need to configure it manually. At the same time, the html-webpack-plugin automatically generates an entry HTML, webpack.config.js content is as follows:
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development'.plugins: [
new HtmlWebpackPlugin({title: 'Actual WASM Rust'})].//
// BREAKING CHANGE: Since webpack 5 WebAssembly is not enabled by default and flagged as experimental feature.
// You need to enable one of the WebAssembly experiments via 'experiments.asyncWebAssembly: true' (based on async modules) or 'experiments.syncWebAssembly: true' (like webpack 4, deprecated).
experiments: {
asyncWebAssembly: true}}Copy the code
3.3 Write code for substitution and test
The implementation calls WebAssembly and adds JS implementations to benchmark.
JS implementation of two methods, one is the ordinary implementation, one is the implementation of tail recursive optimization, SRC /fib.js code:
function fib(i) {
if (i <= 1) return i
return fib(i - 1) + fib(i - 2)}function fibTailCallOptimized(i, prev = 0, next = 1) {
if (i <= 1) return next
return fibTailCallOptimized(i - 1, next, prev + next)
}
export {
fib,
fibTailCallOptimized
}
Copy the code
Call and execute the benchmark, SRC /index.js code:
Import {fib as wasM_fib, FIB_tail_call_optimized as WASm_FIB_tail_call_optimized} from '.. /.. /rust/pkg/rust_bg.wasm' import { fib, fibTailCallOptimized } from './fib.js' function time(timerName, func) { console.time(timerName) console.log(`${timerName}: ', func()) console.timeEnd(timerName)} Const num = 30 time(' wasM_FIB ', () => WASM_FIB (num)) time(' WASM_FIB_tail_call_optimized ', () => wasm_fib_tail_call_optimized(num, 0, 1)) time('fib', () => fib(num)) time('fibTailCallOptimized', () => fibTailCallOptimized(num))Copy the code
Note that the first line references the rust_bg.wASM binary.
Note: browser security policies prohibit loading wasm files using the file:// protocol, so we use webpack-dev-server here.
3.4 Browser running and Benchmark
Webpack packs and runs the test effect in the browser.
$ npx webpack serve
Copy the code
Open it in a browser, usually http://localhost:8080/. Console input is similar to the following (deleted result display, you can check yourself, first to ensure the consistency of the running results) :
Wasm_fib: 0.193115234375 ms WASM_FIB_TAIL_CALL_optimized: MS FIB: 0.93701171875 MS fibTailCallOptimized: 0.16796875 msCopy the code
You can see that WASM is about 5 times more efficient than JS without optimization;
After the tail call optimization is about 2 times.
Reference documentation
- Cargo Chinese document
- Replace the Cargo source – with the currently available domestic Cargo mirror configuration