A comparison between WebAssembly and Javascript made for studying fun.
- WebAssembly with Rust
- Benchmark with fancy charts
- Web Workers to avoid a frozen main thread
- Build and deploy with GitHub Actions
There are two books that fully cover this section:
In this project I'm also using wasm-bingen
and wasm-pack
.
Source code is splitted into two main folders: src-rs
that contains our Rust application and src-js
that contains our Javascript application.
I tried to make their folder structure as similar as possible:
src-rs
├── libs
│ ├── mod.rs
│ ├── ...
│ └── primes.rs
└── main.rs
src-js
├── libs
│ ├── mod.js
│ ├── ...
│ └── primes.js
├── ...
├── bootstrap.js
└── main.js
*/libs/mod.*
is the entry point for declaring all modules. The .rs
version of this file will be compiled to .wasm
.
*/libs/primes.*
is a module example, written both in Rust and Javascript.
*/main.*
is the entry point for the application. The .rs
file is the one used by cargo run
command. The .js
file is the one used by webpack.
./src-js/bootstrap.js
is the bootstrap file for the web application that loads the main.js
file asynchronously.
The first step is to install Rust. We’ll download Rust through rustup
, a command line tool for managing Rust versions and associated tools.
Runnig following command we'll install Rust and Cargo automatically. You will also need to install wasm-pack.
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# install `wasm-pack`
$ curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
# update dependencies
$ cargo update
Now we are able to use the following commands from our project folder.
# test .rs files
$ cargo test
# compile `src-rs/main.rs`
$ cargo build
Finished dev [unoptimized + debuginfo] target(s)
# or compile `src-rs/main.rs` with optimizations
$ cargo build --release
Finished release [optimized] target(s)
Now that we have built our code, we can run it:
$ ./target/release/wasm-vs-js-benchmark primes-get_primes 11
We can also use cargo run
to compile and then run it, all in one step:
$ cargo run primes-get_primes 11
# compile and run our project with optimizations
$ cargo run --release primes-get_primes 11
Last but not least, we'll compile our project to .wasm
:
$ wasm-pack build
Now we are able to use the content of ./pkg
folder for our web application.
The goal of this project is benchmarking WebAssembly and Javascript. Some task will use big computations that takes time to execute.
Web Workers
makes it possible to run a script operation in a background thread separate from the main execution thread of a web application.
The web application is bundled with Webpack
.
Run following command to start it:
$ npm run build:wasm
$ npm start
Project is running at http://localhost:8080/
You can run unit test for Rust and Javascript, simply running npm test
.
I use mocha
for javascript unit test, transpiling ES6 with @babel/core
, @babel/preset-env
and @babel/register
.
The execution time of .wasm binaries is just a bit slower than the execution of same native code.
For benchmarking the native code, I use Hyperfine.
These benchmarks are recorded on a MacBook Pro (15-inch, 2016) having these specs:
- macOS Mojave
- 2,6 GHz Intel Core i7 (4 cores)
- 16 GB 2133 MHz LPDDR3
$ cargo build --release
$ hyperfine --warmup 3 --export-markdown BENCHMARK.md \
'./target/release/wasm-vs-js-benchmark primes-get_primes 100000' \
'./target/release/wasm-vs-js-benchmark matrix-multiply 500 500'
Command | Mean [s] | Min…Max [s] |
---|---|---|
./target/release/wasm-vs-js-benchmark primes-get_primes 100000 |
1.211 ± 0.018 | 1.196…1.255 |
./target/release/wasm-vs-js-benchmark matrix-multiply 500 500 |
0.435 ± 0.016 | 0.417…0.469 |