wasm_bench_runtime 0.2.0

benchmarker for in browser wasm
Documentation
#![warn(missing_docs)]
use std::hint::black_box;

async fn next_animation_frame() {
    let (tx, rx) = futures::channel::oneshot::channel();

    let handle = gloo::render::request_animation_frame(move |_| {
        let _ = tx.send(());
    });
    rx.await.expect("Failed to await rAF");
    drop(handle);
}

fn create_output(msg: &str, i: u32) {
    let window = web_sys::window().unwrap();
    let document = window.document().unwrap();

    let result = document.create_element("div").unwrap();
    result
        .set_attribute("id", &format!("wasm_bench_result_{i}"))
        .unwrap();
    result.set_text_content(Some(msg));

    let body = document.body().unwrap();
    body.append_child(&result).unwrap();
}
fn mark_finished() {
    let window = web_sys::window().unwrap();
    let document = window.document().unwrap();

    let result = document.create_element("div").unwrap();
    result.set_attribute("id", "wasm_bench_done").unwrap();

    let body = document.body().unwrap();
    body.append_child(&result).unwrap();
}

#[derive(Default)]
pub struct Bencher {
    index: u32,
}

impl Bencher {
    pub fn start<R: Future<Output = ()> + 'static>(inner: impl FnOnce(Self) -> R) {
        wasm_bindgen_futures::spawn_local(inner(Self { index: 0 }));
    }

    pub async fn bench<A: Clone, R>(&mut self, name: &str, arg: A, func: impl Fn(A) -> R) {
        let performance = web_sys::window().unwrap().performance().unwrap();

        let start = performance.now();
        let _ = func(black_box(arg.clone()));
        next_animation_frame().await;
        next_animation_frame().await;
        let mut end = performance.now();

        if start == end {
            end += 1.0;
        }
        let target_iterations = 5000.0_f64.div_euclid(end - start) as u128;

        let mut total_rust = 0.0;
        let mut total_ms = 0.0;
        for _ in 0..target_iterations {
            let start = performance.now();
            let _ = func(black_box(arg.clone()));
            let end_rust = performance.now();
            next_animation_frame().await;
            next_animation_frame().await;
            let end = performance.now();

            total_ms += end - start;
            total_rust += end_rust - start;
        }

        let average = total_ms / target_iterations as f64;
        let average_rust = total_rust / target_iterations as f64;

        create_output(
            &format!(
                "{name} (iters: {target_iterations}): {average_rust:.4}ms (with reflow/repaint: {average}ms)"
            ),
            self.index,
        );
        self.index += 1;
    }
}

impl Drop for Bencher {
    fn drop(&mut self) {
        mark_finished();
    }
}