diff --git a/Cargo.lock b/Cargo.lock index 840c7748c..600524243 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1460,6 +1460,7 @@ dependencies = [ name = "wasmer-middleware-common" version = "0.4.2" dependencies = [ + "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-clif-backend 0.4.2", "wasmer-llvm-backend 0.4.2", diff --git a/lib/middleware-common/Cargo.toml b/lib/middleware-common/Cargo.toml index be5517fbb..d6f7c2c4b 100644 --- a/lib/middleware-common/Cargo.toml +++ b/lib/middleware-common/Cargo.toml @@ -15,8 +15,13 @@ wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.2", [dev-dependencies] wabt = "0.7.4" +criterion = "0.2" [features] clif = [] llvm = ["wasmer-llvm-backend"] -singlepass = ["wasmer-singlepass-backend"] \ No newline at end of file +singlepass = ["wasmer-singlepass-backend"] + +[[bench]] +name = "metering_benchmark" +harness = false \ No newline at end of file diff --git a/lib/middleware-common/benches/metering_benchmark.rs b/lib/middleware-common/benches/metering_benchmark.rs new file mode 100644 index 000000000..588842968 --- /dev/null +++ b/lib/middleware-common/benches/metering_benchmark.rs @@ -0,0 +1,231 @@ +#[macro_use] +extern crate criterion; + +use criterion::black_box; +use criterion::{Benchmark, Criterion}; + +use wabt::wat2wasm; + +use wasmer_middleware_common::metering::Metering; +use wasmer_runtime_core::vm::Ctx; +use wasmer_runtime_core::{backend::Compiler, compile_with, imports, Func}; + +//export function add_to(x: i32, y: i32): i32 { +// for(var i = 0; i < x; i++){ +// if(i % 1 == 0){ +// y += i; +// } else { +// y *= i +// } +// } +// return y; +//} +static WAT: &'static str = r#" + (module + (type $t0 (func (param i32 i32) (result i32))) + (type $t1 (func)) + (func $add_to (export "add_to") (type $t0) (param $p0 i32) (param $p1 i32) (result i32) + (local $l0 i32) + block $B0 + i32.const 0 + set_local $l0 + loop $L1 + get_local $l0 + get_local $p0 + i32.lt_s + i32.eqz + br_if $B0 + get_local $l0 + i32.const 1 + i32.rem_s + i32.const 0 + i32.eq + if $I2 + get_local $p1 + get_local $l0 + i32.add + set_local $p1 + else + get_local $p1 + get_local $l0 + i32.mul + set_local $p1 + end + get_local $l0 + i32.const 1 + i32.add + set_local $l0 + br $L1 + unreachable + end + unreachable + end + get_local $p1) + (func $f1 (type $t1)) + (table $table (export "table") 1 anyfunc) + (memory $memory (export "memory") 0) + (global $g0 i32 (i32.const 8)) + (elem (i32.const 0) $f1)) + "#; + +static WAT_GAS: &'static str = r#" + (module + (type $t0 (func (param i32 i32) (result i32))) + (type $t1 (func)) + (type $t2 (func (param i32))) + (import "env" "gas" (func $env.gas (type $t2))) + (func $add_to (type $t0) (param $p0 i32) (param $p1 i32) (result i32) + (local $l0 i32) + i32.const 3 + call $env.gas + block $B0 + i32.const 5 + call $env.gas + i32.const 0 + set_local $l0 + loop $L1 + i32.const 18 + call $env.gas + get_local $l0 + get_local $p0 + i32.lt_s + i32.eqz + br_if $B0 + get_local $l0 + i32.const 1 + i32.rem_s + i32.const 0 + i32.eq + if $I2 + i32.const 5 + call $env.gas + get_local $p1 + get_local $l0 + i32.add + set_local $p1 + else + i32.const 5 + call $env.gas + get_local $p1 + get_local $l0 + i32.mul + set_local $p1 + end + get_local $l0 + i32.const 1 + i32.add + set_local $l0 + br $L1 + unreachable + end + unreachable + end + get_local $p1) + (func $f2 (type $t1) + i32.const 1 + call $env.gas) + (table $table 1 anyfunc) + (memory $memory 0) + (global $g0 i32 (i32.const 8)) + (export "memory" (memory 0)) + (export "table" (table 0)) + (export "add_to" (func $add_to)) + (elem (i32.const 0) $f2)) + "#; + +#[cfg(feature = "llvm")] +fn get_compiler(limit: u64, metering: bool) -> impl Compiler { + use wasmer_llvm_backend::code::LLVMModuleCodeGenerator; + use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler}; + let c: StreamingCompiler = + StreamingCompiler::new(move || { + let mut chain = MiddlewareChain::new(); + if metering { + chain.push(Metering::new(limit)); + } + chain + }); + + c +} + +#[cfg(feature = "singlepass")] +fn get_compiler(limit: u64, metering: bool) -> impl Compiler { + use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler}; + use wasmer_singlepass_backend::ModuleCodeGenerator as SinglePassMCG; + let c: StreamingCompiler = StreamingCompiler::new(move || { + let mut chain = MiddlewareChain::new(); + if metering { + chain.push(Metering::new(limit)); + } + chain + }); + c +} + +#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] +fn get_compiler(_limit: u64, metering: bool) -> impl Compiler { + panic!("compiler not specified, activate a compiler via features"); + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() +} + +#[cfg(feature = "clif")] +fn get_compiler(_limit: u64, metering: bool) -> impl Compiler { + panic!("cranelift does not implement metering"); + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() +} + +fn gas(ctx: &mut Ctx, gas_amount: u32) { + use wasmer_middleware_common::metering; + // TODO read gas field + let used = 100; + let new = used + gas_amount; + () +} + +fn bench_metering(c: &mut Criterion) { + use wasmer_middleware_common::metering; + + c.bench( + "Meter", + Benchmark::new("No Metering", |b| { + let compiler = get_compiler(0, false); + let wasm_binary = wat2wasm(WAT).unwrap(); + let module = compile_with(&wasm_binary, &compiler).unwrap(); + let import_object = imports! {}; + let mut instance = module.instantiate(&import_object).unwrap(); + let add_to: Func<(i32, i32), i32> = instance.func("add_to").unwrap(); + b.iter(|| add_to.call(100, 4).unwrap()) + }) + .with_function("Gas Metering", |b| { + let compiler = get_compiler(0, false); + let gas_wasm_binary = wat2wasm(WAT_GAS).unwrap(); + let gas_module = compile_with(&gas_wasm_binary, &compiler).unwrap(); + let gas_import_object = imports! { + "env" => { + "gas" => Func::new(gas), + }, + }; + let mut gas_instance = gas_module.instantiate(&gas_import_object).unwrap(); + let gas_add_to: Func<(i32, i32), i32> = gas_instance.func("add_to").unwrap(); + b.iter(|| gas_add_to.call(100, 4).unwrap()) + }) + .with_function("Built-in Metering", |b| { + let metering_compiler = get_compiler(std::u64::MAX, true); + let wasm_binary = wat2wasm(WAT).unwrap(); + let metering_module = compile_with(&wasm_binary, &metering_compiler).unwrap(); + let metering_import_object = imports! {}; + let mut metering_instance = metering_module + .instantiate(&metering_import_object) + .unwrap(); + metering::set_points_used(&mut metering_instance, 0u64); + let metering_add_to: Func<(i32, i32), i32> = metering_instance.func("add_to").unwrap(); + b.iter(|| metering_add_to.call(100, 4).unwrap()) + }), + ); +} + +criterion_group!(benches, bench_metering); +criterion_main!(benches);