benchmark interpreter reuse
name without-reuse.trace ns/iter with-reuse.trace ns/iter diff ns/iter diff % speedup bench_regex_redux 3,214,008 3,162,089 -51,919 -1.62% x 1.02 bench_rev_comp 7,356,860 7,168,441 -188,419 -2.56% x 1.03 bench_tiny_keccak 3,943,358 3,910,487 -32,871 -0.83% x 1.01 fac_opt 10,959 2,096 -8,863 -80.87% x 5.23 fac_recursive 13,345 4,277 -9,068 -67.95% x 3.12 recursive_ok 1,190,562 1,141,940 -48,622 -4.08% x 1.04 recursive_trap 125,047 111,962 -13,085 -10.46% x 1.12
This commit is contained in:
parent
2f7f809fd6
commit
1913702a03
|
@ -8,9 +8,10 @@ extern crate wabt;
|
|||
|
||||
use std::error;
|
||||
use std::fs::File;
|
||||
use test::Bencher;
|
||||
use wasmi::{ImportsBuilder, Module, ModuleInstance, NopExternals, RuntimeValue};
|
||||
|
||||
use test::Bencher;
|
||||
mod reuse;
|
||||
|
||||
// Load a module from a file.
|
||||
fn load_from_file(filename: &str) -> Result<Module, Box<error::Error>> {
|
||||
|
@ -26,9 +27,8 @@ 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 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")
|
||||
|
@ -48,9 +48,8 @@ fn bench_tiny_keccak(b: &mut Bencher) {
|
|||
|
||||
#[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 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")
|
||||
|
@ -74,7 +73,8 @@ fn bench_rev_comp(b: &mut Bencher) {
|
|||
);
|
||||
|
||||
// Copy test data inside the wasm memory.
|
||||
let memory = instance.export_by_name("memory")
|
||||
let memory = instance
|
||||
.export_by_name("memory")
|
||||
.expect("Expected export with a name 'memory'")
|
||||
.as_memory()
|
||||
.expect("'memory' should be a memory instance")
|
||||
|
@ -103,9 +103,8 @@ fn bench_rev_comp(b: &mut Bencher) {
|
|||
|
||||
#[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 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")
|
||||
|
@ -129,7 +128,8 @@ fn bench_regex_redux(b: &mut Bencher) {
|
|||
);
|
||||
|
||||
// Copy test data inside the wasm memory.
|
||||
let memory = instance.export_by_name("memory")
|
||||
let memory = instance
|
||||
.export_by_name("memory")
|
||||
.expect("Expected export with a name 'memory'")
|
||||
.as_memory()
|
||||
.expect("'memory' should be a memory instance")
|
||||
|
@ -148,7 +148,7 @@ fn bench_regex_redux(b: &mut Bencher) {
|
|||
#[bench]
|
||||
fn fac_recursive(b: &mut Bencher) {
|
||||
let wasm = wabt::wat2wasm(
|
||||
r#"
|
||||
r#"
|
||||
;; Recursive factorial
|
||||
(func (export "fac-rec") (param i64) (result i64)
|
||||
(if (result i64) (i64.eq (get_local 0) (i64.const 0))
|
||||
|
@ -158,7 +158,7 @@ r#"
|
|||
)
|
||||
)
|
||||
)
|
||||
"#
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let module = Module::from_buffer(&wasm).unwrap();
|
||||
|
@ -168,8 +168,7 @@ r#"
|
|||
.assert_no_start();
|
||||
|
||||
b.iter(|| {
|
||||
let value = instance
|
||||
.invoke_export("fac-rec", &[RuntimeValue::I64(25)], &mut NopExternals);
|
||||
let value = instance.invoke_export("fac-rec", &[RuntimeValue::I64(25)], &mut NopExternals);
|
||||
assert_matches!(value, Ok(Some(RuntimeValue::I64(7034535277573963776))));
|
||||
});
|
||||
}
|
||||
|
@ -177,7 +176,7 @@ r#"
|
|||
#[bench]
|
||||
fn fac_opt(b: &mut Bencher) {
|
||||
let wasm = wabt::wat2wasm(
|
||||
r#"
|
||||
r#"
|
||||
;; Optimized factorial.
|
||||
(func (export "fac-opt") (param i64) (result i64)
|
||||
(local i64)
|
||||
|
@ -192,7 +191,7 @@ r#"
|
|||
)
|
||||
(get_local 1)
|
||||
)
|
||||
"#
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let module = Module::from_buffer(&wasm).unwrap();
|
||||
|
@ -202,8 +201,7 @@ r#"
|
|||
.assert_no_start();
|
||||
|
||||
b.iter(|| {
|
||||
let value = instance
|
||||
.invoke_export("fac-opt", &[RuntimeValue::I64(25)], &mut NopExternals);
|
||||
let value = instance.invoke_export("fac-opt", &[RuntimeValue::I64(25)], &mut NopExternals);
|
||||
assert_matches!(value, Ok(Some(RuntimeValue::I64(7034535277573963776))));
|
||||
});
|
||||
}
|
||||
|
@ -228,7 +226,7 @@ fn recursive_ok(b: &mut Bencher) {
|
|||
end
|
||||
)
|
||||
)
|
||||
"#
|
||||
"#,
|
||||
).unwrap();
|
||||
let module = Module::from_buffer(&wasm).unwrap();
|
||||
|
||||
|
@ -237,8 +235,7 @@ fn recursive_ok(b: &mut Bencher) {
|
|||
.assert_no_start();
|
||||
|
||||
b.iter(|| {
|
||||
let value = instance
|
||||
.invoke_export("call", &[RuntimeValue::I32(8000)], &mut NopExternals);
|
||||
let value = instance.invoke_export("call", &[RuntimeValue::I32(8000)], &mut NopExternals);
|
||||
assert_matches!(value, Ok(Some(RuntimeValue::I32(0))));
|
||||
});
|
||||
}
|
||||
|
@ -262,7 +259,7 @@ fn recursive_trap(b: &mut Bencher) {
|
|||
unreachable
|
||||
)
|
||||
)
|
||||
"#
|
||||
"#,
|
||||
).unwrap();
|
||||
let module = Module::from_buffer(&wasm).unwrap();
|
||||
|
||||
|
@ -271,8 +268,7 @@ fn recursive_trap(b: &mut Bencher) {
|
|||
.assert_no_start();
|
||||
|
||||
b.iter(|| {
|
||||
let value = instance
|
||||
.invoke_export("call", &[RuntimeValue::I32(1000)], &mut NopExternals);
|
||||
let value = instance.invoke_export("call", &[RuntimeValue::I32(1000)], &mut NopExternals);
|
||||
assert_matches!(value, Err(_));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,310 @@
|
|||
use std::error;
|
||||
use std::fs::File;
|
||||
use test::Bencher;
|
||||
use wasmi::{
|
||||
FuncInstance, ImportsBuilder, Interpreter, Module, ModuleInstance, NopExternals, RuntimeValue, StackSize,
|
||||
StackWithLimit,
|
||||
};
|
||||
|
||||
// Load a module from a file.
|
||||
fn load_from_file(filename: &str) -> Result<Module, Box<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)?)
|
||||
}
|
||||
|
||||
fn load_kernel() -> Module {
|
||||
load_from_file("./wasm-kernel/target/wasm32-unknown-unknown/release/wasm_kernel.wasm")
|
||||
.expect("failed to load wasm_kernel. Is `build.rs` broken?")
|
||||
}
|
||||
|
||||
const REVCOMP_INPUT: &'static [u8] = include_bytes!("./revcomp-input.txt");
|
||||
const REVCOMP_OUTPUT: &'static [u8] = include_bytes!("./revcomp-output.txt");
|
||||
|
||||
fn new_interpreter() -> Interpreter {
|
||||
let value_stack = StackWithLimit::with_size(StackSize::from_element_count(1024 * 1024));
|
||||
let call_stack = StackWithLimit::with_size(StackSize::from_element_count(1024 * 1024));
|
||||
Interpreter::new(value_stack, call_stack)
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_tiny_keccak(b: &mut Bencher) {
|
||||
let wasm_kernel = load_kernel();
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
let func = instance.export_by_name("bench_tiny_keccak").unwrap();
|
||||
let func = func.as_func().unwrap();
|
||||
let mut interpreter = new_interpreter();
|
||||
|
||||
b.iter(|| {
|
||||
interpreter.reset();
|
||||
FuncInstance::invoke_configurable(&func, &[test_data_ptr], &mut NopExternals, &mut interpreter).unwrap()
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_rev_comp(b: &mut Bencher) {
|
||||
let wasm_kernel = load_kernel();
|
||||
|
||||
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");
|
||||
|
||||
let func = instance.export_by_name("bench_rev_complement").unwrap();
|
||||
let func = func.as_func().unwrap();
|
||||
let mut interpreter = new_interpreter();
|
||||
|
||||
b.iter(|| {
|
||||
interpreter.reset();
|
||||
FuncInstance::invoke_configurable(&func, &[test_data_ptr], &mut NopExternals, &mut interpreter).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_kernel();
|
||||
|
||||
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");
|
||||
|
||||
let func = instance.export_by_name("bench_regex_redux").unwrap();
|
||||
let func = func.as_func().unwrap();
|
||||
let mut interpreter = new_interpreter();
|
||||
|
||||
b.iter(|| {
|
||||
interpreter.reset();
|
||||
FuncInstance::invoke_configurable(&func, &[test_data_ptr], &mut NopExternals, &mut interpreter).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();
|
||||
|
||||
let func = instance.export_by_name("fac-rec").unwrap();
|
||||
let func = func.as_func().unwrap();
|
||||
let mut interpreter = new_interpreter();
|
||||
|
||||
b.iter(|| {
|
||||
interpreter.reset();
|
||||
let value =
|
||||
FuncInstance::invoke_configurable(&func, &[RuntimeValue::I64(25)], &mut NopExternals, &mut interpreter)
|
||||
.unwrap();
|
||||
assert_matches!(value, 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();
|
||||
|
||||
let func = instance.export_by_name("fac-opt").unwrap();
|
||||
let func = func.as_func().unwrap();
|
||||
let mut interpreter = new_interpreter();
|
||||
|
||||
b.iter(|| {
|
||||
interpreter.reset();
|
||||
let value =
|
||||
FuncInstance::invoke_configurable(&func, &[RuntimeValue::I64(25)], &mut NopExternals, &mut interpreter)
|
||||
.unwrap();
|
||||
assert_matches!(value, 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();
|
||||
|
||||
let func = instance.export_by_name("call").unwrap();
|
||||
let func = func.as_func().unwrap();
|
||||
let mut interpreter = new_interpreter();
|
||||
|
||||
b.iter(|| {
|
||||
interpreter.reset();
|
||||
let value =
|
||||
FuncInstance::invoke_configurable(&func, &[RuntimeValue::I32(8000)], &mut NopExternals, &mut interpreter)
|
||||
.unwrap();
|
||||
assert_matches!(value, 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();
|
||||
|
||||
let func = instance.export_by_name("call").unwrap();
|
||||
let func = func.as_func().unwrap();
|
||||
let mut interpreter = new_interpreter();
|
||||
|
||||
b.iter(|| {
|
||||
interpreter.reset();
|
||||
FuncInstance::invoke_configurable(&func, &[RuntimeValue::I32(1000)], &mut NopExternals, &mut interpreter)
|
||||
.unwrap_err();
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue