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:
Alex Crichton 2019-04-29 13:32:01 -07:00
parent a7b85362ce
commit e4fd0fccb5
13 changed files with 897 additions and 7 deletions

View File

@ -48,6 +48,7 @@ wasm-bindgen-test-crate-b = { path = 'tests/crates/b', version = '0.1' }
[workspace]
members = [
"benchmarks",
"crates/cli",
"crates/js-sys",
"crates/macro/ui-tests",

View File

@ -144,15 +144,9 @@ jobs:
steps:
- template: ci/azure-install-rust.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
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: |
for dir in `ls examples | grep -v README | grep -v asm.js | grep -v raytrace | grep -v without-a-bundler`; do
(cd examples/$dir &&
@ -190,6 +184,19 @@ jobs:
artifactName: examples2
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
displayName: "Dist Linux binary"
steps:
@ -292,6 +299,7 @@ jobs:
- dist_windows
- build_examples
- build_raytrace
- build_benchmarks
displayName: "Deploy everything"
steps:
- template: ci/azure-install-rust.yml
@ -315,6 +323,11 @@ jobs:
inputs:
artifactName: examples2
targetPath: gh-pages/exbuild/raytrace-parallel
- task: DownloadPipelineArtifact@0
displayName: "Download benchmarks"
inputs:
artifactName: benchmarks
targetPath: gh-pages/benchmarks
- task: DownloadPipelineArtifact@0
displayName: "Download dist - windows"
inputs:

11
benchmarks/Cargo.toml Normal file
View 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
View 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
View 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
View 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
View 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);

View 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

Binary file not shown.

52
benchmarks/raw.wast Normal file
View 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
View 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
View 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;
}
}

View 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"