tinywasm 0.9.0

A tiny WebAssembly interpreter
Documentation
use criterion::measurement::WallTime;
use criterion::{BatchSize, BenchmarkGroup, BenchmarkId, Criterion, criterion_group, criterion_main};
use std::hint::black_box;
use tinywasm::{LinearMemory, PagedMemory, VecMemory};

const PAGE_SIZE: usize = 64 * 1024;
const CHUNK_SIZE: usize = 4 * 1024;
const GROW_STEPS: usize = 32;
const BENCH_MEASUREMENT_TIME: std::time::Duration = std::time::Duration::from_secs(10);

const MEMORY_LEN: usize = PAGE_SIZE * 4;
const CONTIGUOUS_OFFSET: usize = 1024;
const CONTIGUOUS_LEN: usize = 2048;
const CROSS_CHUNK_OFFSET: usize = CHUNK_SIZE - 512;
const CROSS_CHUNK_LEN: usize = CHUNK_SIZE * 2;

fn bench_grow<M, F>(group: &mut BenchmarkGroup<'_, WallTime>, backend: &str, make_memory: F)
where
    M: LinearMemory,
    F: Fn() -> M + Copy,
{
    group.bench_function(BenchmarkId::new("grow", backend), |b| {
        b.iter_batched(
            make_memory,
            |mut memory| {
                for page_count in 2..=GROW_STEPS + 1 {
                    memory.grow_to(page_count * PAGE_SIZE).unwrap();
                }
                black_box(memory.len())
            },
            BatchSize::SmallInput,
        )
    });
}

fn bench_write_all<M: LinearMemory>(
    group: &mut BenchmarkGroup<'_, WallTime>,
    backend: &str,
    workload: &str,
    mut memory: M,
    offset: usize,
    len: usize,
) {
    let src = vec![0xA5; len];
    group.bench_function(BenchmarkId::new(format!("write_all/{workload}"), backend), |b| {
        b.iter(|| {
            memory.write_all(offset, black_box(&src)).unwrap();
            black_box(memory.len())
        })
    });
}

fn bench_read_exact<M: LinearMemory>(
    group: &mut BenchmarkGroup<'_, WallTime>,
    backend: &str,
    workload: &str,
    mut memory: M,
    offset: usize,
    len: usize,
) {
    let src = vec![0x5A; len];
    memory.write_all(offset, &src).unwrap();

    let mut dst = vec![0; len];
    group.bench_function(BenchmarkId::new(format!("read_exact/{workload}"), backend), |b| {
        b.iter(|| {
            memory.read_exact(offset, black_box(&mut dst)).unwrap();
            black_box(&dst);
        })
    });
}

fn criterion_benchmark(c: &mut Criterion) {
    let mut group = c.benchmark_group("memory_backends");
    group.measurement_time(BENCH_MEASUREMENT_TIME);

    bench_grow(&mut group, "vec", || VecMemory::try_new(PAGE_SIZE).expect("bench memory should be constructible"));
    bench_grow(&mut group, "paged", || {
        PagedMemory::try_new(PAGE_SIZE, CHUNK_SIZE).expect("bench memory should be constructible")
    });

    bench_write_all(
        &mut group,
        "vec",
        "contiguous",
        VecMemory::try_new(MEMORY_LEN).expect("bench memory should be constructible"),
        CONTIGUOUS_OFFSET,
        CONTIGUOUS_LEN,
    );
    bench_write_all(
        &mut group,
        "paged",
        "contiguous",
        PagedMemory::try_new(MEMORY_LEN, CHUNK_SIZE).expect("bench memory should be constructible"),
        CONTIGUOUS_OFFSET,
        CONTIGUOUS_LEN,
    );
    bench_read_exact(
        &mut group,
        "vec",
        "contiguous",
        VecMemory::try_new(MEMORY_LEN).expect("bench memory should be constructible"),
        CONTIGUOUS_OFFSET,
        CONTIGUOUS_LEN,
    );
    bench_read_exact(
        &mut group,
        "paged",
        "contiguous",
        PagedMemory::try_new(MEMORY_LEN, CHUNK_SIZE).expect("bench memory should be constructible"),
        CONTIGUOUS_OFFSET,
        CONTIGUOUS_LEN,
    );

    bench_write_all(
        &mut group,
        "vec",
        "cross_chunk",
        VecMemory::try_new(MEMORY_LEN).expect("bench memory should be constructible"),
        CROSS_CHUNK_OFFSET,
        CROSS_CHUNK_LEN,
    );
    bench_write_all(
        &mut group,
        "paged",
        "cross_chunk",
        PagedMemory::try_new(MEMORY_LEN, CHUNK_SIZE).expect("bench memory should be constructible"),
        CROSS_CHUNK_OFFSET,
        CROSS_CHUNK_LEN,
    );
    bench_read_exact(
        &mut group,
        "vec",
        "cross_chunk",
        VecMemory::try_new(MEMORY_LEN).expect("bench memory should be constructible"),
        CROSS_CHUNK_OFFSET,
        CROSS_CHUNK_LEN,
    );
    bench_read_exact(
        &mut group,
        "paged",
        "cross_chunk",
        PagedMemory::try_new(MEMORY_LEN, CHUNK_SIZE).expect("bench memory should be constructible"),
        CROSS_CHUNK_OFFSET,
        CROSS_CHUNK_LEN,
    );

    group.finish();
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);