diff options
| author | Henry Gressmann <mail@henrygressmann.de> | 2024-01-30 01:12:38 +0100 |
|---|---|---|
| committer | Henry Gressmann <mail@henrygressmann.de> | 2024-01-30 01:12:38 +0100 |
| commit | 893396aa3ce270280a2a1a87007a4b84edc8b898 (patch) | |
| tree | 59690b8bee97a158a01dd49f4b6e611bdf284546 | |
| parent | 4919d8c2f86f69f669a1afba2a28d5c344fe7197 (diff) | |
perf: add more benchmarks
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
| -rw-r--r-- | Cargo.lock | 52 | ||||
| -rw-r--r-- | Cargo.toml | 6 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | benches/argon2id.rs | 60 | ||||
| -rw-r--r-- | benches/fibonacci.rs | 42 | ||||
| -rw-r--r-- | benches/selfhosted.rs | 30 | ||||
| -rw-r--r-- | benches/util/mod.rs | 10 | ||||
| -rw-r--r-- | examples/rust/Cargo.toml | 5 | ||||
| -rwxr-xr-x | examples/rust/build.sh | 4 | ||||
| -rw-r--r-- | examples/rust/src/argon2id.rs | 14 | ||||
| -rw-r--r-- | examples/rust/src/fibonacci.rs | 8 | ||||
| -rw-r--r-- | examples/rust/src/print.rs | 7 |
12 files changed, 218 insertions, 22 deletions
@@ -120,6 +120,18 @@ dependencies = [ ] [[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + +[[package]] name = "arrayvec" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -164,6 +176,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] name = "bincode" version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -197,6 +215,15 @@ dependencies = [ ] [[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -855,6 +882,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -1653,6 +1681,17 @@ dependencies = [ ] [[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] name = "paste" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1842,6 +1881,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] name = "rayon" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2209,6 +2254,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2349,6 +2400,7 @@ dependencies = [ name = "tinywasm-root" version = "0.0.0" dependencies = [ + "argon2", "color-eyre", "criterion", "tinywasm", @@ -33,6 +33,11 @@ harness=false name="fibonacci" harness=false + +[[bench]] +name="argon2id" +harness=false + [profile.bench] opt-level=3 lto="thin" @@ -48,3 +53,4 @@ wat={version="1.0"} wasmi={version="0.31", features=["std"]} wasmer={version="4.2", features=["cranelift", "singlepass"]} wasmtime={version="17.0", features=["cranelift"]} +argon2={version="0.5"} @@ -22,7 +22,7 @@ TinyWasm, starting from version `0.3.0`, passes all the WebAssembly 1.0 tests in Some APIs to interact with the runtime are not yet exposed, and the existing ones are subject to change, but the core functionality is mostly complete. -TinyWasm is not designed for performance, but rather for simplicity, size and portability. However, it is still reasonably fast, especially when compared to other interpreters. See [Performance](#performance) for more details. +TinyWasm is not (yet) designed for performance, but rather for simplicity, size and portability. See [Performance](#performance) for more details. ## Supported Proposals diff --git a/benches/argon2id.rs b/benches/argon2id.rs new file mode 100644 index 0000000..4504812 --- /dev/null +++ b/benches/argon2id.rs @@ -0,0 +1,60 @@ +mod util; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use util::wasm_to_twasm; + +fn run_tinywasm(twasm: &[u8], params: (i32, i32, i32), name: &str) { + let (mut store, instance) = util::tinywasm(twasm); + let argon2 = instance.exported_func::<(i32, i32, i32), i32>(&store, name).expect("exported_func"); + argon2.call(&mut store, params).expect("call"); +} + +fn run_wasmi(wasm: &[u8], params: (i32, i32, i32), name: &str) { + let (module, mut store, linker) = util::wasmi(wasm); + let instance = linker.instantiate(&mut store, &module).expect("instantiate").start(&mut store).expect("start"); + let argon2 = instance.get_typed_func::<(i32, i32, i32), i32>(&mut store, name).expect("get_typed_func"); + argon2.call(&mut store, params).expect("call"); +} + +fn run_wasmer(wasm: &[u8], params: (i32, i32, i32), name: &str) { + use wasmer::Value; + let (mut store, instance) = util::wasmer(wasm); + let argon2 = instance.exports.get_function(name).expect("get_function"); + argon2.call(&mut store, &[Value::I32(params.0), Value::I32(params.1), Value::I32(params.2)]).expect("call"); +} + +fn run_native(params: (i32, i32, i32)) { + fn run_native(m_cost: i32, t_cost: i32, p_cost: i32) { + let password = b"password"; + let salt = b"some random salt"; + + let params = argon2::Params::new(m_cost as u32, t_cost as u32, p_cost as u32, None).unwrap(); + let argon = argon2::Argon2::new(argon2::Algorithm::Argon2id, argon2::Version::V0x13, params); + + let mut hash = [0u8; 32]; + argon.hash_password_into(password, salt, &mut hash).unwrap(); + } + run_native(params.0, params.1, params.2) +} + +const ARGON2ID: &[u8] = include_bytes!("../examples/rust/out/argon2id.wasm"); +fn criterion_benchmark(c: &mut Criterion) { + let twasm = wasm_to_twasm(ARGON2ID); + let params = (1000, 2, 1); + + let mut group = c.benchmark_group("argon2id"); + group.measurement_time(std::time::Duration::from_secs(7)); + group.sample_size(10); + + group.bench_function("native", |b| b.iter(|| run_native(black_box(params)))); + group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm, black_box(params), "argon2id"))); + group.bench_function("wasmi", |b| b.iter(|| run_wasmi(&ARGON2ID, black_box(params), "argon2id"))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(&ARGON2ID, black_box(params), "argon2id"))); +} + +criterion_group!( + name = benches; + config = Criterion::default().significance_level(0.1); + targets = criterion_benchmark +); + +criterion_main!(benches); diff --git a/benches/fibonacci.rs b/benches/fibonacci.rs index ca83869..1d1134a 100644 --- a/benches/fibonacci.rs +++ b/benches/fibonacci.rs @@ -4,15 +4,45 @@ use util::wasm_to_twasm; fn run_tinywasm(twasm: &[u8], iterations: i32, name: &str) { let (mut store, instance) = util::tinywasm(twasm); - let hello = instance.exported_func::<i32, i32>(&store, name).expect("exported_func"); - hello.call(&mut store, iterations).expect("call"); + let fib = instance.exported_func::<i32, i32>(&store, name).expect("exported_func"); + fib.call(&mut store, iterations).expect("call"); } fn run_wasmi(wasm: &[u8], iterations: i32, name: &str) { let (module, mut store, linker) = util::wasmi(wasm); let instance = linker.instantiate(&mut store, &module).expect("instantiate").start(&mut store).expect("start"); - let hello = instance.get_typed_func::<i32, i32>(&mut store, name).expect("get_typed_func"); - hello.call(&mut store, iterations).expect("call"); + let fib = instance.get_typed_func::<i32, i32>(&mut store, name).expect("get_typed_func"); + fib.call(&mut store, iterations).expect("call"); +} + +fn run_wasmer(wasm: &[u8], iterations: i32, name: &str) { + use wasmer::*; + let engine: Engine = wasmer::Singlepass::default().into(); + let mut store = Store::default(); + let import_object = imports! {}; + let module = wasmer::Module::from_binary(&engine, &wasm).expect("wasmer::Module::from_binary"); + let instance = Instance::new(&mut store, &module, &import_object).expect("Instance::new"); + let fib = instance.exports.get_typed_function::<i32, i32>(&mut store, name).expect("get_function"); + fib.call(&mut store, iterations).expect("call"); +} + +fn run_native(n: i32) -> i32 { + let mut sum = 0; + let mut last = 0; + let mut curr = 1; + for _i in 1..n { + sum = last + curr; + last = curr; + curr = sum; + } + sum +} + +fn run_native_recursive(n: i32) -> i32 { + if n <= 1 { + return n; + } + run_native_recursive(n - 1) + run_native_recursive(n - 2) } const FIBONACCI: &[u8] = include_bytes!("../examples/rust/out/fibonacci.wasm"); @@ -21,15 +51,19 @@ fn criterion_benchmark(c: &mut Criterion) { { let mut group = c.benchmark_group("fibonacci"); + group.bench_function("native", |b| b.iter(|| run_native(black_box(60)))); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm, black_box(60), "fibonacci"))); group.bench_function("wasmi", |b| b.iter(|| run_wasmi(&FIBONACCI, black_box(60), "fibonacci"))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(&FIBONACCI, black_box(60), "fibonacci"))); } { let mut group = c.benchmark_group("fibonacci-recursive"); group.measurement_time(std::time::Duration::from_secs(5)); + group.bench_function("native", |b| b.iter(|| run_native_recursive(black_box(26)))); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm, black_box(26), "fibonacci_recursive"))); group.bench_function("wasmi", |b| b.iter(|| run_wasmi(&FIBONACCI, black_box(26), "fibonacci_recursive"))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(&FIBONACCI, black_box(26), "fibonacci_recursive"))); } } diff --git a/benches/selfhosted.rs b/benches/selfhosted.rs index 4464579..57146e7 100644 --- a/benches/selfhosted.rs +++ b/benches/selfhosted.rs @@ -3,6 +3,17 @@ use criterion::{criterion_group, criterion_main, Criterion}; use crate::util::twasm_to_module; +fn run_native() { + use tinywasm::*; + let module = tinywasm::Module::parse_bytes(include_bytes!("../examples/rust/out/print.wasm")).expect("parse"); + let mut store = Store::default(); + let mut imports = Imports::default(); + imports.define("env", "printi32", Extern::typed_func(|_: FuncContext<'_>, _: i32| Ok(()))).expect("define"); + let instance = ModuleInstance::instantiate(&mut store, module, Some(imports)).expect("instantiate"); + let hello = instance.exported_func::<(i32, i32), ()>(&store, "add_and_print").expect("exported_func"); + hello.call(&mut store, (2, 3)).expect("call"); +} + fn run_tinywasm(twasm: &[u8]) { use tinywasm::*; let module = twasm_to_module(twasm); @@ -26,18 +37,35 @@ fn run_wasmi(wasm: &[u8]) { hello.call(&mut store, ()).expect("call"); } +fn run_wasmer(wasm: &[u8]) { + use wasmer::*; + let engine = wasmer::Engine::default(); + let mut store = Store::default(); + let import_object = imports! { + "env" => { + "printi32" => Function::new_typed(&mut store, |_: i32| {}), + }, + }; + let module = wasmer::Module::from_binary(&engine, &wasm).expect("wasmer::Module::from_binary"); + let instance = Instance::new(&mut store, &module, &import_object).expect("Instance::new"); + let hello = instance.exports.get_function("hello").expect("get_function"); + hello.call(&mut store, &[]).expect("call"); +} + const TINYWASM: &[u8] = include_bytes!("../examples/rust/out/tinywasm.wasm"); fn criterion_benchmark(c: &mut Criterion) { let twasm = util::wasm_to_twasm(TINYWASM); let mut group = c.benchmark_group("selfhosted"); + group.bench_function("native", |b| b.iter(|| run_native())); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm))); group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); } criterion_group!( name = benches; - config = Criterion::default().sample_size(500).measurement_time(std::time::Duration::from_secs(5)).significance_level(0.1); + config = Criterion::default().sample_size(100).measurement_time(std::time::Duration::from_secs(5)).significance_level(0.1); targets = criterion_benchmark ); diff --git a/benches/util/mod.rs b/benches/util/mod.rs index 69510a5..aa7b418 100644 --- a/benches/util/mod.rs +++ b/benches/util/mod.rs @@ -30,3 +30,13 @@ pub fn wasmi(wasm: &[u8]) -> (wasmi::Module, wasmi::Store<()>, wasmi::Linker<()> let linker = <Linker<()>>::new(&engine); (module, store, linker) } + +pub fn wasmer(wasm: &[u8]) -> (wasmer::Store, wasmer::Instance) { + use wasmer::*; + let engine: Engine = wasmer::Singlepass::default().into(); + let mut store = Store::default(); + let import_object = imports! {}; + let module = wasmer::Module::from_binary(&engine, &wasm).expect("wasmer::Module::from_binary"); + let instance = Instance::new(&mut store, &module, &import_object).expect("Instance::new"); + (store, instance) +} diff --git a/examples/rust/Cargo.toml b/examples/rust/Cargo.toml index 2d06488..f3f475e 100644 --- a/examples/rust/Cargo.toml +++ b/examples/rust/Cargo.toml @@ -11,6 +11,7 @@ edition="2021" [dependencies] tinywasm={path="../../crates/tinywasm", features=["parser", "std", "unsafe"]} +argon2={version="0.5"} [[bin]] name="hello" @@ -28,6 +29,10 @@ path="src/tinywasm.rs" name="fibonacci" path="src/fibonacci.rs" +[[bin]] +name="argon2id" +path="src/argon2id.rs" + [profile.wasm] opt-level=3 lto="thin" diff --git a/examples/rust/build.sh b/examples/rust/build.sh index cabf00e..e6d3b0d 100755 --- a/examples/rust/build.sh +++ b/examples/rust/build.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash cd "$(dirname "$0")" -bins=("hello" "fibonacci" "print" "tinywasm") +bins=("hello" "fibonacci" "print" "tinywasm" "argon2id") exclude_wat=("tinywasm") out_dir="./target/wasm32-unknown-unknown/wasm" dest_dir="out" @@ -15,7 +15,7 @@ for bin in "${bins[@]}"; do RUSTFLAGS="-C target-feature=$features -C panic=abort" cargo build --target wasm32-unknown-unknown --package rust-wasm-examples --profile=wasm --bin "$bin" cp "$out_dir/$bin.wasm" "$dest_dir/" - wasm-opt "$dest_dir/$bin.wasm" -o "$dest_dir/$bin.wasm" -O --intrinsic-lowering -O + wasm-opt "$dest_dir/$bin.wasm" -o "$dest_dir/$bin.wasm" -Oz --enable-bulk-memory --enable-multivalue --enable-reference-types --enable-mutable-globals if [[ ! " ${exclude_wat[@]} " =~ " $bin " ]]; then wasm2wat "$dest_dir/$bin.wasm" -o "$dest_dir/$bin.wat" diff --git a/examples/rust/src/argon2id.rs b/examples/rust/src/argon2id.rs new file mode 100644 index 0000000..01ea7ca --- /dev/null +++ b/examples/rust/src/argon2id.rs @@ -0,0 +1,14 @@ +#![no_main] + +#[no_mangle] +pub extern "C" fn argon2id(m_cost: i32, t_cost: i32, p_cost: i32) -> i32 { + let password = b"password"; + let salt = b"some random salt"; + + let params = argon2::Params::new(m_cost as u32, t_cost as u32, p_cost as u32, None).unwrap(); + let argon = argon2::Argon2::new(argon2::Algorithm::Argon2id, argon2::Version::V0x13, params); + + let mut hash = [0u8; 32]; + argon.hash_password_into(password, salt, &mut hash).unwrap(); + hash[0] as i32 +} diff --git a/examples/rust/src/fibonacci.rs b/examples/rust/src/fibonacci.rs index 7924aef..8de496d 100644 --- a/examples/rust/src/fibonacci.rs +++ b/examples/rust/src/fibonacci.rs @@ -1,11 +1,5 @@ -#![no_std] #![no_main] - -#[cfg(not(test))] -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - core::arch::wasm32::unreachable() -} +#![allow(non_snake_case)] #[no_mangle] pub extern "C" fn fibonacci(n: i32) -> i32 { diff --git a/examples/rust/src/print.rs b/examples/rust/src/print.rs index 34f3c7f..d04daa3 100644 --- a/examples/rust/src/print.rs +++ b/examples/rust/src/print.rs @@ -1,12 +1,5 @@ -#![no_std] #![no_main] -#[cfg(not(test))] -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - core::arch::wasm32::unreachable() -} - #[link(wasm_import_module = "env")] extern "C" { fn printi32(x: i32); |
