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-generateThe 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.rsCode 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

  1. Cargo Chinese document
  2. Replace the Cargo source – with the currently available domestic Cargo mirror configuration