mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-15 09:40:52 +00:00
Add a page of microbenchmarks for wasm-bindgen
This commit starts to add a page of microbenchmarks for wasm-bindgen which we can hopefully track and compare over time. Right now it's primarily focused on data collection, making it easy to collect data across a number of benchmarks for comparison. It doesn't currently do much in the way of actually comparing the results for you (aka drawing pretty graphs), so let's left for a future step. It's hoped though that we can use this to track performance improvements as well as ensuring that they work over time!
This commit is contained in:
parent
a7b85362ce
commit
e4fd0fccb5
@ -48,6 +48,7 @@ wasm-bindgen-test-crate-b = { path = 'tests/crates/b', version = '0.1' }
|
|||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
|
"benchmarks",
|
||||||
"crates/cli",
|
"crates/cli",
|
||||||
"crates/js-sys",
|
"crates/js-sys",
|
||||||
"crates/macro/ui-tests",
|
"crates/macro/ui-tests",
|
||||||
|
@ -144,15 +144,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- template: ci/azure-install-rust.yml
|
- template: ci/azure-install-rust.yml
|
||||||
- template: ci/azure-install-sccache.yml
|
- template: ci/azure-install-sccache.yml
|
||||||
|
- template: ci/azure-install-wasm-pack.yml
|
||||||
- script: mv _package.json package.json && npm install && rm package.json
|
- script: mv _package.json package.json && npm install && rm package.json
|
||||||
displayName: "run npm install"
|
displayName: "run npm install"
|
||||||
- script: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
|
|
||||||
displayName: "install wasm-pack"
|
|
||||||
- script: |
|
|
||||||
set -ex
|
|
||||||
cargo build -p wasm-bindgen-cli
|
|
||||||
ln -snf `pwd`/target/debug/wasm-bindgen $HOME/.cargo/bin/wasm-bindgen
|
|
||||||
displayName: "install wasm-bindgen for `wasm-pack` to use"
|
|
||||||
- script: |
|
- script: |
|
||||||
for dir in `ls examples | grep -v README | grep -v asm.js | grep -v raytrace | grep -v without-a-bundler`; do
|
for dir in `ls examples | grep -v README | grep -v asm.js | grep -v raytrace | grep -v without-a-bundler`; do
|
||||||
(cd examples/$dir &&
|
(cd examples/$dir &&
|
||||||
@ -190,6 +184,19 @@ jobs:
|
|||||||
artifactName: examples2
|
artifactName: examples2
|
||||||
targetPath: '$(Build.ArtifactStagingDirectory)'
|
targetPath: '$(Build.ArtifactStagingDirectory)'
|
||||||
|
|
||||||
|
- job: build_benchmarks
|
||||||
|
displayName: "Build benchmarks"
|
||||||
|
steps:
|
||||||
|
- template: ci/azure-install-rust.yml
|
||||||
|
- template: ci/azure-install-sccache.yml
|
||||||
|
- template: ci/azure-install-wasm-pack.yml
|
||||||
|
- script: wasm-pack build --target web benchmarks
|
||||||
|
displayName: "build benchmarks"
|
||||||
|
- task: PublishPipelineArtifact@0
|
||||||
|
inputs:
|
||||||
|
artifactName: benchmarks
|
||||||
|
targetPath: benchmarks
|
||||||
|
|
||||||
- job: dist_linux
|
- job: dist_linux
|
||||||
displayName: "Dist Linux binary"
|
displayName: "Dist Linux binary"
|
||||||
steps:
|
steps:
|
||||||
@ -292,6 +299,7 @@ jobs:
|
|||||||
- dist_windows
|
- dist_windows
|
||||||
- build_examples
|
- build_examples
|
||||||
- build_raytrace
|
- build_raytrace
|
||||||
|
- build_benchmarks
|
||||||
displayName: "Deploy everything"
|
displayName: "Deploy everything"
|
||||||
steps:
|
steps:
|
||||||
- template: ci/azure-install-rust.yml
|
- template: ci/azure-install-rust.yml
|
||||||
@ -315,6 +323,11 @@ jobs:
|
|||||||
inputs:
|
inputs:
|
||||||
artifactName: examples2
|
artifactName: examples2
|
||||||
targetPath: gh-pages/exbuild/raytrace-parallel
|
targetPath: gh-pages/exbuild/raytrace-parallel
|
||||||
|
- task: DownloadPipelineArtifact@0
|
||||||
|
displayName: "Download benchmarks"
|
||||||
|
inputs:
|
||||||
|
artifactName: benchmarks
|
||||||
|
targetPath: gh-pages/benchmarks
|
||||||
- task: DownloadPipelineArtifact@0
|
- task: DownloadPipelineArtifact@0
|
||||||
displayName: "Download dist - windows"
|
displayName: "Download dist - windows"
|
||||||
inputs:
|
inputs:
|
||||||
|
11
benchmarks/Cargo.toml
Normal file
11
benchmarks/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "wasm-bindgen-benchmark"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["The wasm-bindgen Developers"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
wasm-bindgen = "0.2.43"
|
||||||
|
web-sys = { version = "0.3.20", features = ['Node'] }
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ['cdylib']
|
54
benchmarks/README.md
Normal file
54
benchmarks/README.md
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# Microbenchmarks for `wasm-bindgen`
|
||||||
|
|
||||||
|
This folder houses a number of microbenchmarks for `wasm-bindgen`. These, like
|
||||||
|
all microbenchmarks, should be taken with a grain of salt. They are intended to
|
||||||
|
help developers understand changes over time, but they are not intended to be a
|
||||||
|
performance suite for WebAssembly for Rust.
|
||||||
|
|
||||||
|
[View benchmarks for `master` branch online][online]
|
||||||
|
|
||||||
|
[online]: https://rustwasm.github.io/wasm-bindgen/benchmarks/
|
||||||
|
|
||||||
|
## Building and Running
|
||||||
|
|
||||||
|
First, copy the benchmarks to a temporary directory:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cp ./benchmarks /some/other/directory
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, `cd` into that directory and execute:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ wasm-pack build --target web
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, use your favorite static file server to host the current directory. For
|
||||||
|
example using the [`https` crate](https://crates.io/crates/https):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ http
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open up a web browser and view http://localhost:8000, for example.
|
||||||
|
|
||||||
|
You should be presented a page with lots of `(run)` links, where when you click
|
||||||
|
them it will execute the benchmark and then display the result.
|
||||||
|
|
||||||
|
## Benchmark Architecture
|
||||||
|
|
||||||
|
Currently benchmarks are pretty bare bones. They just use benchmark.js to
|
||||||
|
generate statistics which are then rendered to the screen. Benchmarks are listed
|
||||||
|
one-by-one in `index.html` where a `td` exists for each benchmark. In `index.js`
|
||||||
|
each of the `td`'s `id` properties are hooked up to an actual function to
|
||||||
|
benchmark, depending on what's being benchmarked.
|
||||||
|
|
||||||
|
Relevant files are:
|
||||||
|
|
||||||
|
* `index.html` - the page showing all benchmarks
|
||||||
|
* `index.js` - the driver JS for all benchmarks
|
||||||
|
* `globals.js` - global JS functions imported by all other benchmarks
|
||||||
|
* `js-bencharks.js` - the JS functions that we're benchmarking
|
||||||
|
* `src/lib.rs` - the Rust/`wasm-bindgen` functions we're benchmarking
|
||||||
|
* `raw.wast`/`raw.wasm` - a raw handwritten WebAssembly file used in some
|
||||||
|
benchmarks. A compiled version of this is checked into the repository.
|
5
benchmarks/globals.js
Normal file
5
benchmarks/globals.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export function jsthunk() {}
|
||||||
|
export function add(a, b) { return a + b; }
|
||||||
|
export class Foo {
|
||||||
|
bar() {}
|
||||||
|
}
|
295
benchmarks/index.html
Normal file
295
benchmarks/index.html
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
body {
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
thead td {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
table td {
|
||||||
|
border: 1px solid black;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src='index.js' type=module async></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a href='https://github.com/alexcrichton/rust-wasm-benchmark'>Source code</a>
|
||||||
|
|
||||||
|
<h1>JS / wasm-bindgen comparison</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
These benchmarks are meant to compare WebAssembly costs using raw wasm
|
||||||
|
files and wasm-bindgen itself against the JS equivalents. These
|
||||||
|
microbenchmarks aren't really representative of WebAssembly performance,
|
||||||
|
but can be useful data points about how expensive it is to cross
|
||||||
|
boundaries for example.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For all benchmarks higher numbers are better numbers.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<table id='js-vs-wasm-bindgen'>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Benchmark</td>
|
||||||
|
<td>wasm-bindgen</td>
|
||||||
|
<td>JS</td>
|
||||||
|
<td><code>*.wast</code></td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Call a thunk
|
||||||
|
|
||||||
|
<a class='about-open' href='#'>(?)</a>
|
||||||
|
|
||||||
|
<p class='about'>
|
||||||
|
This benchmarks tests out how long it take JS to call a thunk in
|
||||||
|
the given language. For example JS will call a JS thunk or
|
||||||
|
JS will call a wasm function that does nothing.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class='bm' id='wbindgen_thunk'></td>
|
||||||
|
<td class='bm' id='js_thunk'></td>
|
||||||
|
<td class='bm' id='raw_thunk'></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Call an adder
|
||||||
|
|
||||||
|
<a class='about-open' href='#'>(?)</a>
|
||||||
|
|
||||||
|
<p class='about'>
|
||||||
|
This benchmarks tests out how long it take JS to call a function
|
||||||
|
in the target language which adds two numbers and returns the
|
||||||
|
result. This is likely to be similar to the previous benchmark
|
||||||
|
in terms of results, but it's thought that some extra computation
|
||||||
|
may show overheads differently.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class='bm' id='wbindgen_thunk'></td>
|
||||||
|
<td class='bm' id='js_thunk'></td>
|
||||||
|
<td class='bm' id='raw_thunk'></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
Call a JS thunk
|
||||||
|
|
||||||
|
<a class='about-open' href='#'>(?)</a>
|
||||||
|
|
||||||
|
<p class='about'>
|
||||||
|
This benchmarks tests out how long it takes to call a JS function
|
||||||
|
10,000 times. Each language/framework has a function which takes a
|
||||||
|
parameter of how many times to call an imported JS function.
|
||||||
|
Remember that JS itself benefits from inlining, where as JS cannot
|
||||||
|
be inlined into WebAssembly. In these cases the imported function
|
||||||
|
performs no work, it just returns immediately.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class='bm' id='wbindgen_call_js_thunk_n_times'></td>
|
||||||
|
<td class='bm' id='js_call_js_thunk_n_times'></td>
|
||||||
|
<td class='bm' id='raw_call_js_thunk_n_times'></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Call a JS function which adds
|
||||||
|
|
||||||
|
<a class='about-open' href='#'>(?)</a>
|
||||||
|
|
||||||
|
<p class='about'>
|
||||||
|
This benchmark is similar to the previous thunk benchmark except
|
||||||
|
that this time the imported function will add its two arguments
|
||||||
|
and returns the result. This helps measure the overhead of
|
||||||
|
sending data like integers back and forth.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class='bm' id='wbindgen_call_js_add_n_times'></td>
|
||||||
|
<td class='bm' id='js_call_js_add_n_times'></td>
|
||||||
|
<td class='bm' id='raw_call_js_add_n_times'></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Calculate Fib(40)
|
||||||
|
|
||||||
|
<a class='about-open' href='#'>(?)</a>
|
||||||
|
|
||||||
|
<p class='about'>
|
||||||
|
This benchmarks calculates the 40th fibonacci number. It in
|
||||||
|
theory should favor wasm since wasm is "better a compute", but
|
||||||
|
a good JIT will probably make the code roughly equivalent.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class='bm' id='wbindgen_fib_40'></td>
|
||||||
|
<td class='bm' id='js_fib_40'></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Access <code>Node.firstChild</code>
|
||||||
|
|
||||||
|
<a class='about-open' href='#'>(?)</a>
|
||||||
|
|
||||||
|
<p class='about'>
|
||||||
|
This benchmark attempts to see the cost of accessing a DOM
|
||||||
|
property from both JS and WebAssembly. We access the DOM property
|
||||||
|
as fast as possible in WebAssembly and otherwise just access it
|
||||||
|
as a normal property in JS.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class='bm' id='wbindgen_call_node_first_child_n_times'></td>
|
||||||
|
<td class='bm' id='js_call_node_first_child_n_times'></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Access <code>Node.nodeType</code>
|
||||||
|
|
||||||
|
<a class='about-open' href='#'>(?)</a>
|
||||||
|
|
||||||
|
<p class='about'>
|
||||||
|
This benchmark attempts to see the cost of accessing a DOM
|
||||||
|
property from both JS and WebAssembly. We access the DOM property
|
||||||
|
as fast as possible in WebAssembly and otherwise just access it
|
||||||
|
as a normal property in JS.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class='bm' id='wbindgen_call_node_node_type_n_times'></td>
|
||||||
|
<td class='bm' id='js_call_node_node_type_n_times'></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Count types of nodes on a page
|
||||||
|
|
||||||
|
<a class='about-open' href='#'>(?)</a>
|
||||||
|
|
||||||
|
<p class='about'>
|
||||||
|
This is intended to be a "flavorful DOM benchmark" which
|
||||||
|
exercises DOM functionality from WebAssembly, specifically
|
||||||
|
counting the number of types of each node on a page.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class='bm' id='wbindgen_count_node_types'></td>
|
||||||
|
<td class='bm' id='js_count_node_types'></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h1>wasm-bindgen benchmarks</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
These benchmarks don't compare against JS but are instead more intended
|
||||||
|
to be compared against each other, across browsers, or across versions of
|
||||||
|
wasm-bindgen.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<table id='js-vs-wasm-bindgen'>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Benchmark</td>
|
||||||
|
<td>Result</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id='wbindgen-body'>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Access <code>Node.nodeType</code> with <code>final</code>
|
||||||
|
|
||||||
|
<a class='about-open' href='#'>(?)</a>
|
||||||
|
|
||||||
|
<p class='about'>
|
||||||
|
This is similar to the <code>Node.nodeType</code> benchmark above
|
||||||
|
except that it uses the <code>final</code> attribute in
|
||||||
|
wasm-bindgen.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class='bm' id='wbindgen_call_first_child_final_n_times'></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Access <code>Node.nodeType</code> with <code>structural</code>
|
||||||
|
|
||||||
|
<a class='about-open' href='#'>(?)</a>
|
||||||
|
|
||||||
|
<p class='about'>
|
||||||
|
This is similar to the <code>Node.nodeType</code> benchmark above
|
||||||
|
except that it uses the <code>structural</code> attribute in
|
||||||
|
wasm-bindgen.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class='bm' id='wbindgen_call_first_child_structural_n_times'></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Call a custom JS class <code>Foo.bar</code> method with
|
||||||
|
<code>final</code>
|
||||||
|
|
||||||
|
<a class='about-open' href='#'>(?)</a>
|
||||||
|
|
||||||
|
<p class='about'>
|
||||||
|
This is similar to the <code>Node.nodeType</code> benchmark above
|
||||||
|
except that it's not calling a DOM method but rather a custom JS
|
||||||
|
class's method.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class='bm' id='wbindgen_call_foo_bar_final_n_times'></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Call a custom JS class <code>Foo.bar</code> method with
|
||||||
|
<code>structural</code>
|
||||||
|
|
||||||
|
<a class='about-open' href='#'>(?)</a>
|
||||||
|
|
||||||
|
<p class='about'>
|
||||||
|
This is similar to the <code>Node.nodeType</code> benchmark above
|
||||||
|
except that it's not calling a DOM method but rather a custom JS
|
||||||
|
class's method.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class='bm' id='wbindgen_call_foo_bar_structural_n_times'></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr style='display:none' class='str-benchmark'>
|
||||||
|
<td>
|
||||||
|
Pass <span class='str'></span> to/from wasm-bindgen
|
||||||
|
|
||||||
|
<a class='about-open' href='#'>(?)</a>
|
||||||
|
|
||||||
|
<p class='about'>
|
||||||
|
This benchmarks the overhead of passing strings to wasm and
|
||||||
|
also receiving them from wasm.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class='bm'></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
182
benchmarks/index.js
Normal file
182
benchmarks/index.js
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
// Benchmarking framework that we're using
|
||||||
|
import 'https://unpkg.com/lodash@4.17.11/lodash.js';
|
||||||
|
import 'https://unpkg.com/benchmark@2.1.4/benchmark.js';
|
||||||
|
|
||||||
|
// Import lots of functions from JS/wasm, and rename everything to have the
|
||||||
|
// namespace of where it's coming from.
|
||||||
|
import wbindgen_init, {
|
||||||
|
call_js_thunk_n_times as wbindgen_call_js_thunk_n_times,
|
||||||
|
call_js_add_n_times as wbindgen_call_js_add_n_times,
|
||||||
|
thunk as wbindgen_thunk,
|
||||||
|
add as wbindgen_add,
|
||||||
|
fibonacci as wbindgen_fibonacci,
|
||||||
|
call_node_first_child_n_times as wbindgen_call_node_first_child_n_times,
|
||||||
|
call_node_node_type_n_times as wbindgen_call_node_node_type_n_times,
|
||||||
|
count_node_types as wbindgen_count_node_types,
|
||||||
|
call_first_child_final_n_times as wbindgen_call_first_child_final_n_times,
|
||||||
|
call_first_child_structural_n_times as wbindgen_call_first_child_structural_n_times,
|
||||||
|
call_foo_bar_final_n_times as wbindgen_call_foo_bar_final_n_times,
|
||||||
|
call_foo_bar_structural_n_times as wbindgen_call_foo_bar_structural_n_times,
|
||||||
|
str_roundtrip as wbindgen_str_roundtrip,
|
||||||
|
} from './pkg/wasm_bindgen_benchmark.js';
|
||||||
|
import {
|
||||||
|
call_js_thunk_n_times as js_call_js_thunk_n_times,
|
||||||
|
call_js_add_n_times as js_call_js_add_n_times,
|
||||||
|
thunk as js_thunk,
|
||||||
|
add as js_add,
|
||||||
|
fibonacci as js_fibonacci,
|
||||||
|
call_node_first_child_n_times as js_call_node_first_child_n_times,
|
||||||
|
call_node_node_type_n_times as js_call_node_node_type_n_times,
|
||||||
|
count_node_types as js_count_node_types,
|
||||||
|
} from './js-benchmarks.js';
|
||||||
|
import * as globals from './globals.js';
|
||||||
|
import { Lock } from './utils.js';
|
||||||
|
|
||||||
|
// These are set for `raw.wasm`, which we import and configure manually:
|
||||||
|
let raw_call_js_thunk_n_times = null;
|
||||||
|
let raw_call_js_add_n_times = null;
|
||||||
|
let raw_thunk = null;
|
||||||
|
let raw_add = null;
|
||||||
|
|
||||||
|
// Create a `Map` of all benchmarks that we're going to execute, where the map
|
||||||
|
// is from a benchmark's name to a thunk to execute for the benchmark.
|
||||||
|
function makeBenchmarks() {
|
||||||
|
const benchmarks = new Map();
|
||||||
|
|
||||||
|
benchmarks.wbindgen_thunk = wbindgen_thunk;
|
||||||
|
benchmarks.raw_thunk = raw_thunk;
|
||||||
|
benchmarks.js_thunk = js_thunk;
|
||||||
|
|
||||||
|
benchmarks.wbindgen_fib_40 = () => wbindgen_fibonacci(40);
|
||||||
|
benchmarks.js_fib_40 = () => js_fibonacci(40);
|
||||||
|
|
||||||
|
benchmarks.wbindgen_add = () => wbindgen_add(2, 3);
|
||||||
|
benchmarks.raw_add = () => raw_add(2, 3);
|
||||||
|
benchmarks.js_add = () => js_add(2, 3);
|
||||||
|
|
||||||
|
benchmarks.js_call_js_thunk_n_times = () => js_call_js_thunk_n_times(10000);
|
||||||
|
benchmarks.raw_call_js_thunk_n_times = () => raw_call_js_thunk_n_times(10000);
|
||||||
|
benchmarks.wbindgen_call_js_thunk_n_times = () => wbindgen_call_js_thunk_n_times(10000);
|
||||||
|
|
||||||
|
benchmarks.js_call_js_add_n_times = () => js_call_js_add_n_times(10000, 2, 3);
|
||||||
|
benchmarks.raw_call_js_add_n_times = () => raw_call_js_add_n_times(10000, 2, 3);
|
||||||
|
benchmarks.wbindgen_call_js_add_n_times = () => wbindgen_call_js_add_n_times(10000, 2, 3);
|
||||||
|
|
||||||
|
const list = [];
|
||||||
|
for (let i = 0; i < 10; i++)
|
||||||
|
list.push(document.body);
|
||||||
|
benchmarks.wbindgen_call_node_first_child_n_times = () => wbindgen_call_node_first_child_n_times(1000, list);
|
||||||
|
benchmarks.js_call_node_first_child_n_times = () => js_call_node_first_child_n_times(1000, list);
|
||||||
|
benchmarks.wbindgen_call_node_node_type_n_times = () => wbindgen_call_node_node_type_n_times(1000, list);
|
||||||
|
benchmarks.js_call_node_node_type_n_times = () => js_call_node_node_type_n_times(1000, list);
|
||||||
|
|
||||||
|
const body = document.body;
|
||||||
|
benchmarks.wbindgen_count_node_types = () => wbindgen_count_node_types(body);
|
||||||
|
benchmarks.js_count_node_types = () => js_count_node_types(body);
|
||||||
|
|
||||||
|
benchmarks.wbindgen_call_first_child_final_n_times = () => wbindgen_call_first_child_final_n_times(10000, body);
|
||||||
|
benchmarks.wbindgen_call_first_child_structural_n_times = () => wbindgen_call_first_child_structural_n_times(10000, body);
|
||||||
|
|
||||||
|
const foo = new globals.Foo();
|
||||||
|
benchmarks.wbindgen_call_foo_bar_final_n_times = () => wbindgen_call_foo_bar_final_n_times(10000, foo);
|
||||||
|
benchmarks.wbindgen_call_foo_bar_structural_n_times = () => wbindgen_call_foo_bar_structural_n_times(10000, foo);
|
||||||
|
|
||||||
|
|
||||||
|
const strings = {
|
||||||
|
ascii_small: 'ja',
|
||||||
|
ascii_medium: 'aym0566x',
|
||||||
|
ascii_number: '505874924095815681',
|
||||||
|
ascii_date: 'Sun Aug 31 00:29:15 +0000 2014',
|
||||||
|
ascii_url: 'https://pbs.twimg.com/profile_images/497760886795153410/LDjAwR_y_normal.jpeg',
|
||||||
|
ascii_link: '<a href="http://twitter.com/download/iphone" rel="nofollow">Twitter for iPhone</a>',
|
||||||
|
unicode: '@aym0566x \n\n名前:前田あゆみ\n第一印象:なんか怖っ!\n今の印象:とりあえずキモい。噛み合わない\n好きなところ:ぶすでキモいとこ😋✨✨\n思い出:んーーー、ありすぎ😊❤️\nLINE交換できる?:あぁ……ごめん✋\nトプ画をみて:照れますがな😘✨\n一言:お前は一生もんのダチ💖'
|
||||||
|
}
|
||||||
|
const template = document.querySelector('tr.str-benchmark');
|
||||||
|
template.remove();
|
||||||
|
const tbody = document.querySelector('tbody#wbindgen-body');
|
||||||
|
for (const bm in strings) {
|
||||||
|
const s = strings[bm];
|
||||||
|
const bm_name = `wbindgen_str_${bm}`;
|
||||||
|
benchmarks[bm_name] = () => wbindgen_str_roundtrip(s);
|
||||||
|
|
||||||
|
const row = template.cloneNode(true);
|
||||||
|
row.querySelector('.str').textContent = bm;
|
||||||
|
row.querySelector('td.bm').id = bm_name;
|
||||||
|
row.removeAttribute('style');
|
||||||
|
tbody.appendChild(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
return benchmarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the page and initialize all event handlers and such.
|
||||||
|
function run() {
|
||||||
|
const benchmarks = makeBenchmarks();
|
||||||
|
|
||||||
|
const benchmarkLock = new Lock();
|
||||||
|
for (const td of document.querySelectorAll('td.bm')) {
|
||||||
|
const bm = benchmarks[td.id];
|
||||||
|
if (typeof bm !== 'function')
|
||||||
|
throw new Error(`no benchmark registered for ${td.id}`);
|
||||||
|
|
||||||
|
const run = document.createElement('a');
|
||||||
|
run.href = '#';
|
||||||
|
run.innerText = '(run)';
|
||||||
|
run.onclick = function() {
|
||||||
|
benchmarkLock.withLock(async () => {
|
||||||
|
await executeAndUpdate(td.id, bm, td);
|
||||||
|
});
|
||||||
|
run.remove();
|
||||||
|
td.innerText = 'executing ...';
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
td.appendChild(run);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const a of document.querySelectorAll('.about-open')) {
|
||||||
|
a.onclick = function() {
|
||||||
|
a.nextElementSibling.style.display = 'block';
|
||||||
|
a.remove();
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function executeAndUpdate(name, bm, td) {
|
||||||
|
const result = await executeBenchmark(name, bm);
|
||||||
|
console.log(result.target);
|
||||||
|
const rme = Math.round(result.target.stats.rme * 100) / 100;
|
||||||
|
td.innerText = `${Math.round(result.target.hz).toLocaleString()}/s ±${rme}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeBenchmark(name, bm) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const suite = new Benchmark.Suite();
|
||||||
|
suite.add(name, bm);
|
||||||
|
suite.on('cycle', resolve);
|
||||||
|
suite.run({ async: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load wasm files and when they're done (plus the DOM) then we initialize
|
||||||
|
// everything
|
||||||
|
const wasms = [];
|
||||||
|
wasms.push(wbindgen_init('./pkg/wasm_bindgen_benchmark_bg.wasm'));
|
||||||
|
wasms.push(fetch('./raw.wasm')
|
||||||
|
.then(r => r.arrayBuffer())
|
||||||
|
.then(m => WebAssembly.instantiate(m, { './globals.js': globals }))
|
||||||
|
.then(m => {
|
||||||
|
raw_call_js_thunk_n_times = m.instance.exports.call_js_thunk_n_times;
|
||||||
|
raw_call_js_add_n_times = m.instance.exports.call_js_add_n_times;
|
||||||
|
raw_thunk = m.instance.exports.thunk;
|
||||||
|
raw_add = m.instance.exports.add;
|
||||||
|
}));
|
||||||
|
|
||||||
|
Promise.all(wasms)
|
||||||
|
.then(() => {
|
||||||
|
if (document.readyState === 'loading')
|
||||||
|
document.addEventListener('DOMContentLoaded', run);
|
||||||
|
else
|
||||||
|
run();
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
76
benchmarks/js-benchmarks.js
Normal file
76
benchmarks/js-benchmarks.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { jsthunk, add as jsadd } from './globals.js';
|
||||||
|
|
||||||
|
export function fibonacci(n) {
|
||||||
|
let a = 1;
|
||||||
|
let b = 1;
|
||||||
|
let tmp = 0;
|
||||||
|
|
||||||
|
while (n > 1) {
|
||||||
|
tmp = b;
|
||||||
|
b += a;
|
||||||
|
a = tmp;
|
||||||
|
--n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function thunk() {}
|
||||||
|
|
||||||
|
export function call_js_thunk_n_times(n) {
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
jsthunk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function add(a, b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function call_js_add_n_times(n, a, b) {
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
jsadd(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function call_node_first_child_n_times(n, array_of_elements) {
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
for (const element of array_of_elements)
|
||||||
|
if (element.firstChild === null)
|
||||||
|
throw new Error("bad");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function call_node_node_type_n_times(n, array_of_elements) {
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
for (const element of array_of_elements)
|
||||||
|
if (element.nodeType === 100)
|
||||||
|
throw new Error("bad");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function call_node_has_child_nodes_n_times(n, array_of_elements) {
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
for (const element of array_of_elements)
|
||||||
|
if (!element.hasChildNodes())
|
||||||
|
throw new Error("bad");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function count_node_types(element) {
|
||||||
|
const types = [];
|
||||||
|
|
||||||
|
function count(node, types) {
|
||||||
|
while(node) {
|
||||||
|
const type = node.nodeType;
|
||||||
|
while (types.length <= type)
|
||||||
|
types.push(0);
|
||||||
|
types[type] += 1;
|
||||||
|
count(node.firstChild, types);
|
||||||
|
node = node.nextSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count(element, types);
|
||||||
|
return types;
|
||||||
|
}
|
BIN
benchmarks/raw.wasm
Normal file
BIN
benchmarks/raw.wasm
Normal file
Binary file not shown.
52
benchmarks/raw.wast
Normal file
52
benchmarks/raw.wast
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
(module
|
||||||
|
(import "./globals.js" "jsthunk" (func $js_thunk))
|
||||||
|
(import "./globals.js" "add" (func $js_add (param i32) (param i32) (result i32)))
|
||||||
|
|
||||||
|
(export "call_js_thunk_n_times" (func $call_thunk))
|
||||||
|
(export "call_js_add_n_times" (func $call_add))
|
||||||
|
(export "thunk" (func $thunk))
|
||||||
|
(export "add" (func $add))
|
||||||
|
|
||||||
|
(func $call_thunk (param i32)
|
||||||
|
block
|
||||||
|
get_local 0
|
||||||
|
i32.eqz
|
||||||
|
br_if 0
|
||||||
|
loop
|
||||||
|
call $js_thunk
|
||||||
|
get_local 0
|
||||||
|
i32.const 1
|
||||||
|
i32.sub
|
||||||
|
tee_local 0
|
||||||
|
br_if 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
(func $call_add (param i32) (param i32) (param i32)
|
||||||
|
block
|
||||||
|
get_local 0
|
||||||
|
i32.eqz
|
||||||
|
br_if 0
|
||||||
|
loop
|
||||||
|
get_local 2
|
||||||
|
get_local 1
|
||||||
|
call $js_add
|
||||||
|
drop
|
||||||
|
get_local 0
|
||||||
|
i32.const 1
|
||||||
|
i32.sub
|
||||||
|
tee_local 0
|
||||||
|
br_if 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
(func $thunk)
|
||||||
|
|
||||||
|
(func $add (param i32) (param i32) (result i32)
|
||||||
|
get_local 0
|
||||||
|
get_local 1
|
||||||
|
i32.add
|
||||||
|
)
|
||||||
|
)
|
179
benchmarks/src/lib.rs
Normal file
179
benchmarks/src/lib.rs
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
extern crate wasm_bindgen;
|
||||||
|
extern crate web_sys;
|
||||||
|
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
use web_sys::Node;
|
||||||
|
|
||||||
|
#[wasm_bindgen(raw_module = "../globals.js")]
|
||||||
|
extern {
|
||||||
|
#[wasm_bindgen(js_name = jsthunk)]
|
||||||
|
fn js_thunk();
|
||||||
|
#[wasm_bindgen(js_name = add)]
|
||||||
|
fn js_add(a: i32, b: i32) -> i32;
|
||||||
|
|
||||||
|
pub type Foo;
|
||||||
|
#[wasm_bindgen(method, final, js_name = bar)]
|
||||||
|
fn bar_final(this: &Foo);
|
||||||
|
#[wasm_bindgen(method, structural, js_name = bar)]
|
||||||
|
fn bar_structural(this: &Foo);
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = jsthunk)]
|
||||||
|
fn doesnt_throw();
|
||||||
|
#[wasm_bindgen(catch, js_name = jsthunk)]
|
||||||
|
fn doesnt_throw_catch() -> Result<(), JsValue>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn call_js_thunk_n_times(n: usize) {
|
||||||
|
for _ in 0..n {
|
||||||
|
js_thunk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn call_js_add_n_times(n: usize, a: i32, b: i32) {
|
||||||
|
for _ in 0..n {
|
||||||
|
js_add(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn thunk() {}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn add(a: i32, b: i32) -> i32 {
|
||||||
|
a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut FIB_HIGH: i32 = 0;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn fibonacci(n: i32) -> i32 {
|
||||||
|
let mut a = 1u64;
|
||||||
|
let mut b = 1;
|
||||||
|
for _ in 0..n {
|
||||||
|
let tmp = b;
|
||||||
|
b += a;
|
||||||
|
a = tmp;
|
||||||
|
}
|
||||||
|
unsafe { FIB_HIGH = (a >> 32) as i32; }
|
||||||
|
return a as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn fibonacci_high() -> i32 {
|
||||||
|
unsafe { FIB_HIGH }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn call_foo_bar_final_n_times(n: usize, foo: &Foo) {
|
||||||
|
for _ in 0..n {
|
||||||
|
foo.bar_final();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn call_foo_bar_structural_n_times(n: usize, foo: &Foo) {
|
||||||
|
for _ in 0..n {
|
||||||
|
foo.bar_structural();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn call_doesnt_throw_n_times(n: usize) {
|
||||||
|
for _ in 0..n {
|
||||||
|
doesnt_throw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn call_doesnt_throw_with_catch_n_times(n: usize) {
|
||||||
|
for _ in 0..n {
|
||||||
|
if let Err(e) = doesnt_throw_catch() {
|
||||||
|
wasm_bindgen::throw_val(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern {
|
||||||
|
pub type Element;
|
||||||
|
|
||||||
|
#[wasm_bindgen(method, js_name = firstChild, final, getter)]
|
||||||
|
fn first_child_final(this: &Element) -> Element;
|
||||||
|
#[wasm_bindgen(method, js_name = firstChild, structural, getter)]
|
||||||
|
fn first_child_structural(this: &Element) -> Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn call_first_child_final_n_times(n: usize, element: &Element) {
|
||||||
|
for _ in 0..n {
|
||||||
|
drop(element.first_child_final());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn call_first_child_structural_n_times(n: usize, element: &Element) {
|
||||||
|
for _ in 0..n {
|
||||||
|
drop(element.first_child_structural());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn call_node_first_child_n_times(n: usize, elements: Vec<JsValue>) {
|
||||||
|
for _ in 0..n {
|
||||||
|
for element in elements.iter() {
|
||||||
|
let element = element.unchecked_ref::<Node>();
|
||||||
|
assert!(element.first_child().is_some());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn call_node_node_type_n_times(n: usize, elements: Vec<JsValue>) {
|
||||||
|
for _ in 0..n {
|
||||||
|
for element in elements.iter() {
|
||||||
|
let element = element.unchecked_ref::<Node>();
|
||||||
|
assert!(element.node_type() != 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn call_node_has_child_nodes_n_times(n: usize, elements: Vec<JsValue>) {
|
||||||
|
for _ in 0..n {
|
||||||
|
for element in elements.iter() {
|
||||||
|
let element = element.unchecked_ref::<Node>();
|
||||||
|
assert!(element.has_child_nodes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn count_node_types(element: Node) {
|
||||||
|
let mut count = Vec::new();
|
||||||
|
count_node_types(element, &mut count);
|
||||||
|
|
||||||
|
fn count_node_types(mut element: Node, count: &mut Vec<u32>) {
|
||||||
|
loop {
|
||||||
|
let t = element.node_type();
|
||||||
|
if t as usize >= count.len() {
|
||||||
|
count.resize(t as usize + 1, 0);
|
||||||
|
}
|
||||||
|
count[t as usize] += 1;
|
||||||
|
if let Some(s) = element.first_child() {
|
||||||
|
count_node_types(s, count);
|
||||||
|
}
|
||||||
|
match element.next_sibling() {
|
||||||
|
Some(s) => element = s,
|
||||||
|
None => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn str_roundtrip(s: String) -> String {
|
||||||
|
s
|
||||||
|
}
|
14
benchmarks/utils.js
Normal file
14
benchmarks/utils.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export class Lock {
|
||||||
|
constructor() {
|
||||||
|
this.lockHolder = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async withLock(scope) {
|
||||||
|
while (this.lockHolder !== null) {
|
||||||
|
await this.lockHolder;
|
||||||
|
}
|
||||||
|
this.lockHolder = Promise.resolve(null).then(scope);
|
||||||
|
await this.lockHolder;
|
||||||
|
this.lockHolder = null;
|
||||||
|
}
|
||||||
|
}
|
8
ci/azure-install-wasm-pack.yml
Normal file
8
ci/azure-install-wasm-pack.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
steps:
|
||||||
|
- script: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
|
||||||
|
displayName: "install wasm-pack"
|
||||||
|
- script: |
|
||||||
|
set -ex
|
||||||
|
cargo build -p wasm-bindgen-cli
|
||||||
|
ln -snf `pwd`/target/debug/wasm-bindgen $HOME/.cargo/bin/wasm-bindgen
|
||||||
|
displayName: "install wasm-bindgen for `wasm-pack` to use"
|
Loading…
x
Reference in New Issue
Block a user