wasmi/benches/src/lib.rs

279 lines
7.2 KiB
Rust

#![feature(test)]
extern crate test;
extern crate wasmi;
#[macro_use]
extern crate assert_matches;
extern crate wabt;
use std::error;
use std::fs::File;
use wasmi::{ImportsBuilder, Module, ModuleInstance, NopExternals, RuntimeValue};
use test::Bencher;
// Load a module from a file.
fn load_from_file(filename: &str) -> Result<Module, Box<dyn error::Error>> {
use std::io::prelude::*;
let mut file = File::open(filename)?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
Ok(Module::from_buffer(buf)?)
}
const REVCOMP_INPUT: &'static [u8] = include_bytes!("./revcomp-input.txt");
const REVCOMP_OUTPUT: &'static [u8] = include_bytes!("./revcomp-output.txt");
#[bench]
fn bench_tiny_keccak(b: &mut Bencher) {
let wasm_kernel = load_from_file(
"./wasm-kernel/target/wasm32-unknown-unknown/release/wasm_kernel.wasm",
).expect("failed to load wasm_kernel. Is `build.rs` broken?");
let instance = ModuleInstance::new(&wasm_kernel, &ImportsBuilder::default())
.expect("failed to instantiate wasm module")
.assert_no_start();
let test_data_ptr = assert_matches!(
instance.invoke_export("prepare_tiny_keccak", &[], &mut NopExternals),
Ok(Some(v @ RuntimeValue::I32(_))) => v
);
b.iter(|| {
instance
.invoke_export("bench_tiny_keccak", &[test_data_ptr], &mut NopExternals)
.unwrap();
});
}
#[bench]
fn bench_rev_comp(b: &mut Bencher) {
let wasm_kernel = load_from_file(
"./wasm-kernel/target/wasm32-unknown-unknown/release/wasm_kernel.wasm",
).expect("failed to load wasm_kernel. Is `build.rs` broken?");
let instance = ModuleInstance::new(&wasm_kernel, &ImportsBuilder::default())
.expect("failed to instantiate wasm module")
.assert_no_start();
// Allocate buffers for the input and output.
let test_data_ptr: RuntimeValue = {
let input_size = RuntimeValue::I32(REVCOMP_INPUT.len() as i32);
assert_matches!(
instance.invoke_export("prepare_rev_complement", &[input_size], &mut NopExternals),
Ok(Some(v @ RuntimeValue::I32(_))) => v,
"",
)
};
// Get the pointer to the input buffer.
let input_data_mem_offset = assert_matches!(
instance.invoke_export("rev_complement_input_ptr", &[test_data_ptr], &mut NopExternals),
Ok(Some(RuntimeValue::I32(v))) => v as u32,
"",
);
// Copy test data inside the wasm memory.
let memory = instance.export_by_name("memory")
.expect("Expected export with a name 'memory'")
.as_memory()
.expect("'memory' should be a memory instance")
.clone();
memory
.set(input_data_mem_offset, REVCOMP_INPUT)
.expect("can't load test data into a wasm memory");
b.iter(|| {
instance
.invoke_export("bench_rev_complement", &[test_data_ptr], &mut NopExternals)
.unwrap();
});
// Verify the result.
let output_data_mem_offset = assert_matches!(
instance.invoke_export("rev_complement_output_ptr", &[test_data_ptr], &mut NopExternals),
Ok(Some(RuntimeValue::I32(v))) => v as u32,
"",
);
let result = memory
.get(output_data_mem_offset, REVCOMP_OUTPUT.len())
.expect("can't get result data from a wasm memory");
assert_eq!(&*result, REVCOMP_OUTPUT);
}
#[bench]
fn bench_regex_redux(b: &mut Bencher) {
let wasm_kernel = load_from_file(
"./wasm-kernel/target/wasm32-unknown-unknown/release/wasm_kernel.wasm",
).expect("failed to load wasm_kernel. Is `build.rs` broken?");
let instance = ModuleInstance::new(&wasm_kernel, &ImportsBuilder::default())
.expect("failed to instantiate wasm module")
.assert_no_start();
// Allocate buffers for the input and output.
let test_data_ptr: RuntimeValue = {
let input_size = RuntimeValue::I32(REVCOMP_INPUT.len() as i32);
assert_matches!(
instance.invoke_export("prepare_regex_redux", &[input_size], &mut NopExternals),
Ok(Some(v @ RuntimeValue::I32(_))) => v,
"",
)
};
// Get the pointer to the input buffer.
let input_data_mem_offset = assert_matches!(
instance.invoke_export("regex_redux_input_ptr", &[test_data_ptr], &mut NopExternals),
Ok(Some(RuntimeValue::I32(v))) => v as u32,
"",
);
// Copy test data inside the wasm memory.
let memory = instance.export_by_name("memory")
.expect("Expected export with a name 'memory'")
.as_memory()
.expect("'memory' should be a memory instance")
.clone();
memory
.set(input_data_mem_offset, REVCOMP_INPUT)
.expect("can't load test data into a wasm memory");
b.iter(|| {
instance
.invoke_export("bench_regex_redux", &[test_data_ptr], &mut NopExternals)
.unwrap();
});
}
#[bench]
fn fac_recursive(b: &mut Bencher) {
let wasm = wabt::wat2wasm(
r#"
;; Recursive factorial
(func (export "fac-rec") (param i64) (result i64)
(if (result i64) (i64.eq (get_local 0) (i64.const 0))
(then (i64.const 1))
(else
(i64.mul (get_local 0) (call 0 (i64.sub (get_local 0) (i64.const 1))))
)
)
)
"#
).unwrap();
let module = Module::from_buffer(&wasm).unwrap();
let instance = ModuleInstance::new(&module, &ImportsBuilder::default())
.expect("failed to instantiate wasm module")
.assert_no_start();
b.iter(|| {
let value = instance
.invoke_export("fac-rec", &[RuntimeValue::I64(25)], &mut NopExternals);
assert_matches!(value, Ok(Some(RuntimeValue::I64(7034535277573963776))));
});
}
#[bench]
fn fac_opt(b: &mut Bencher) {
let wasm = wabt::wat2wasm(
r#"
;; Optimized factorial.
(func (export "fac-opt") (param i64) (result i64)
(local i64)
(set_local 1 (i64.const 1))
(block
(br_if 0 (i64.lt_s (get_local 0) (i64.const 2)))
(loop
(set_local 1 (i64.mul (get_local 1) (get_local 0)))
(set_local 0 (i64.add (get_local 0) (i64.const -1)))
(br_if 0 (i64.gt_s (get_local 0) (i64.const 1)))
)
)
(get_local 1)
)
"#
).unwrap();
let module = Module::from_buffer(&wasm).unwrap();
let instance = ModuleInstance::new(&module, &ImportsBuilder::default())
.expect("failed to instantiate wasm module")
.assert_no_start();
b.iter(|| {
let value = instance
.invoke_export("fac-opt", &[RuntimeValue::I64(25)], &mut NopExternals);
assert_matches!(value, Ok(Some(RuntimeValue::I64(7034535277573963776))));
});
}
// This is used for testing overhead of a function call
// is not too large.
#[bench]
fn recursive_ok(b: &mut Bencher) {
let wasm = wabt::wat2wasm(
r#"
(module
(func $call (export "call") (param i32) (result i32)
block (result i32)
get_local 0
get_local 0
i32.eqz
br_if 0
i32.const 1
i32.sub
call $call
end
)
)
"#
).unwrap();
let module = Module::from_buffer(&wasm).unwrap();
let instance = ModuleInstance::new(&module, &ImportsBuilder::default())
.expect("failed to instantiate wasm module")
.assert_no_start();
b.iter(|| {
let value = instance
.invoke_export("call", &[RuntimeValue::I32(8000)], &mut NopExternals);
assert_matches!(value, Ok(Some(RuntimeValue::I32(0))));
});
}
#[bench]
fn recursive_trap(b: &mut Bencher) {
let wasm = wabt::wat2wasm(
r#"
(module
(func $call (export "call") (param i32) (result i32)
block (result i32)
get_local 0
get_local 0
i32.eqz
br_if 0
i32.const 1
i32.sub
call $call
end
unreachable
)
)
"#
).unwrap();
let module = Module::from_buffer(&wasm).unwrap();
let instance = ModuleInstance::new(&module, &ImportsBuilder::default())
.expect("failed to instantiate wasm module")
.assert_no_start();
b.iter(|| {
let value = instance
.invoke_export("call", &[RuntimeValue::I32(1000)], &mut NopExternals);
assert_matches!(value, Err(_));
});
}