mirror of
https://github.com/pezkuwichain/wasm-instrument.git
synced 2026-04-22 02:07:58 +00:00
54213f77f6
Updates the requirements on [wasmi](https://github.com/paritytech/wasmi) to permit the latest version. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/paritytech/wasmi/releases">wasmi's releases</a>.</em></p> <blockquote> <h2>v0.21.0 - 2023-01-04</h2> <h3>Added</h3> <ul> <li>Add support for resumable function calls. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/598">paritytech/wasmi#598</a>) <ul> <li>This feature allows to resume a function call upon encountering a host trap.</li> </ul> </li> <li>Add support for concurrently running function executions using a single <code>wasmi</code> engine. <ul> <li>This feature also allows to call Wasm functions from host functions. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/590">paritytech/wasmi#590</a>)</li> </ul> </li> <li>Add initial naive WASI support for <code>wasmi</code> using the new <code>wasmi_wasi</code> crate. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/557">paritytech/wasmi#557</a>) <ul> <li>Special thanks to <a href="https://github.com/OLUWAMUYIWA">Onigbinde Oluwamuyiwa Elijah</a> for carrying the WASI support efforts!</li> <li>Also thanks to <a href="https://github.com/Berrysoft">Yuyi Wang</a> for testing and improving initial WASI support. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/592">paritytech/wasmi#592</a>, <a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/571">paritytech/wasmi#571</a>, <a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/568">paritytech/wasmi#568</a>)</li> <li><strong>Note:</strong> There is ongoing work to integrate WASI support in <code>wasmi_cli</code> so that the <code>wasmi</code> CLI will then be able to execute arbitrary <code>wasm-wasi</code> files out of the box in the future.</li> </ul> </li> <li>Add <code>Module::imports</code> that allows to query Wasm module imports. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/573">paritytech/wasmi#573</a>, <a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/583">paritytech/wasmi#583</a>)</li> </ul> <h3>Fixed</h3> <ul> <li>Fix a bug that imported linear memories and tables were initialized twice upon instantiation. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/593">paritytech/wasmi#593</a>)</li> <li>The <code>wasmi</code> CLI now properly hints for file path arguments. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/596">paritytech/wasmi#596</a>)</li> </ul> <h3>Changed</h3> <ul> <li>The <code>wasmi::Trap</code> type is now more similar to Wasmtime's <code>Trap</code> type. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/559">paritytech/wasmi#559</a>)</li> <li>The <code>wasmi::Store</code> type is now <code>Send</code> and <code>Sync</code> as intended. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/566">paritytech/wasmi#566</a>)</li> <li>The <code>wasmi</code> CLI now prints exported functions names if the function name CLI argument is missing. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/579">paritytech/wasmi#579</a>)</li> <li>Improve feedback when running a Wasm module without exported function using <code>wasmi</code> CLI. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/584">paritytech/wasmi#584</a>)</li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/paritytech/wasmi/blob/master/CHANGELOG.md">wasmi's changelog</a>.</em></p> <blockquote> <h2>[0.21.0] - 2023-01-04</h2> <h3>Added</h3> <ul> <li>Add support for resumable function calls. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/598">paritytech/wasmi#598</a>) <ul> <li>This feature allows to resume a function call upon encountering a host trap.</li> </ul> </li> <li>Add support for concurrently running function executions using a single <code>wasmi</code> engine. <ul> <li>This feature also allows to call Wasm functions from host functions. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/590">paritytech/wasmi#590</a>)</li> </ul> </li> <li>Add initial naive WASI support for <code>wasmi</code> using the new <code>wasmi_wasi</code> crate. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/557">paritytech/wasmi#557</a>) <ul> <li>Special thanks to <a href="https://github.com/OLUWAMUYIWA">Onigbinde Oluwamuyiwa Elijah</a> for carrying the WASI support efforts!</li> <li>Also thanks to <a href="https://github.com/Berrysoft">Yuyi Wang</a> for testing and improving initial WASI support. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/592">paritytech/wasmi#592</a>, <a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/571">paritytech/wasmi#571</a>, <a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/568">paritytech/wasmi#568</a>)</li> <li><strong>Note:</strong> There is ongoing work to integrate WASI support in <code>wasmi_cli</code> so that the <code>wasmi</code> CLI will then be able to execute arbitrary <code>wasm-wasi</code> files out of the box in the future.</li> </ul> </li> <li>Add <code>Module::imports</code> that allows to query Wasm module imports. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/573">paritytech/wasmi#573</a>, <a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/583">paritytech/wasmi#583</a>)</li> </ul> <h3>Fixed</h3> <ul> <li>Fix a bug that imported linear memories and tables were initialized twice upon instantiation. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/593">paritytech/wasmi#593</a>)</li> <li>The <code>wasmi</code> CLI now properly hints for file path arguments. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/596">paritytech/wasmi#596</a>)</li> </ul> <h3>Changed</h3> <ul> <li>The <code>wasmi::Trap</code> type is now more similar to Wasmtime's <code>Trap</code> type. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/559">paritytech/wasmi#559</a>)</li> <li>The <code>wasmi::Store</code> type is now <code>Send</code> and <code>Sync</code> as intended. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/566">paritytech/wasmi#566</a>)</li> <li>The <code>wasmi</code> CLI now prints exported functions names if the function name CLI argument is missing. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/579">paritytech/wasmi#579</a>)</li> <li>Improve feedback when running a Wasm module without exported function using <code>wasmi</code> CLI. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/584">paritytech/wasmi#584</a>)</li> </ul> <h2>[0.20.0] - 2022-11-04</h2> <h3>Added</h3> <ul> <li>Contribution documentation about fuzz testing. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/529">paritytech/wasmi#529</a>)</li> </ul> <h3>Removed</h3> <ul> <li>Removed some deprecated functions in the <code>wasmi_core</code> crate. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/545">paritytech/wasmi#545</a>)</li> </ul> <h3>Fixed</h3> <ul> <li>Fixed a critical performance regression introduced in Rust 1.65. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/518">paritytech/wasmi#518</a>) <ul> <li>While the PR's main job was to clean up some code it was found out that it also fixes a critical performance regression introduced in Rust 1.65.</li> <li>You can read more about this performance regression <a href="https://github-redirect.dependabot.com/rust-lang/rust/issues/102952">in this thread</a>.</li> </ul> </li> </ul> <h3>Changed</h3> <ul> <li>Fixed handling of edge cases with respect to Wasm linear memory. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/449">paritytech/wasmi#449</a>) <ul> <li>This allows for <code>wasmi</code> to properly setup and use linear memory instances of up to 4GB.</li> </ul> </li> <li>Optimize and improve Wasm instantiation. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/531">paritytech/wasmi#531</a>)</li> <li>Optimize <code>global.get</code> of immutable non-imported globals. (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/pull/533">paritytech/wasmi#533</a>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/paritytech/wasmi/commit/9e228e8a253074bf25cfcfeff11b07ff2033170e"><code>9e228e8</code></a> Bump <code>wasmi</code> version to <code>0.21.0</code> (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/issues/604">#604</a>)</li> <li><a href="https://github.com/paritytech/wasmi/commit/8fb3a58de76a10b0072d0c71fd1677a96f33009f"><code>8fb3a58</code></a> Prepare for <code>wasmi</code> version <code>0.21.0</code> release (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/issues/603">#603</a>)</li> <li><a href="https://github.com/paritytech/wasmi/commit/829ae5b115e8d8f484c93798309ac9e1eb7351eb"><code>829ae5b</code></a> Resumable Function Calls (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/issues/598">#598</a>)</li> <li><a href="https://github.com/paritytech/wasmi/commit/3c62031d096a73c59edb05ea5cc508e5bc65b0f6"><code>3c62031</code></a> Add <code>res: EngineResources</code> field to <code>EngineExecutor</code> (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/issues/602">#602</a>)</li> <li><a href="https://github.com/paritytech/wasmi/commit/bbcfc21deea430f82aac6c268ffea61587173060"><code>bbcfc21</code></a> Refactor <code>Engine</code> internals (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/issues/601">#601</a>)</li> <li><a href="https://github.com/paritytech/wasmi/commit/3186c023210a537d0fd70517942a1ebd0c5ad3f7"><code>3186c02</code></a> Return <code>()</code> from <code>CallResults</code> for slices (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/issues/600">#600</a>)</li> <li><a href="https://github.com/paritytech/wasmi/commit/8965c9af96beb189919c0a2aa306a8ae111141ad"><code>8965c9a</code></a> Refactor FuncType verification (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/issues/599">#599</a>)</li> <li><a href="https://github.com/paritytech/wasmi/commit/2b5a48618fa9acb1d36e91ba9325623d6858e0e6"><code>2b5a486</code></a> CLI: Improve CLI parsing (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/issues/596">#596</a>)</li> <li><a href="https://github.com/paritytech/wasmi/commit/da8e4a694054ea218b316c31f28aae5494a42119"><code>da8e4a6</code></a> Allow host functions to call Wasm functions (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/issues/590">#590</a>)</li> <li><a href="https://github.com/paritytech/wasmi/commit/f82ed77000b520046918b4e75cd733a2a7650d5b"><code>f82ed77</code></a> Fix duplicated imported linear memories and tables (<a href="https://github-redirect.dependabot.com/paritytech/wasmi/issues/593">#593</a>)</li> <li>Additional commits viewable in <a href="https://github.com/paritytech/wasmi/compare/v0.20.0...v0.21.0">compare view</a></li> </ul> </details> <br /> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alexander Theißen <alex.theissen@me.com>
575 lines
19 KiB
Rust
575 lines
19 KiB
Rust
use criterion::{
|
|
criterion_group, criterion_main, measurement::Measurement, Bencher, BenchmarkGroup, Criterion,
|
|
Throughput,
|
|
};
|
|
use std::{
|
|
fs::{read, read_dir},
|
|
path::PathBuf,
|
|
slice,
|
|
};
|
|
use wasm_instrument::{
|
|
gas_metering::{self, host_function, mutable_global, Backend, ConstantCostRules},
|
|
inject_stack_limiter,
|
|
parity_wasm::{deserialize_buffer, elements::Module, serialize},
|
|
};
|
|
|
|
fn fixture_dir() -> PathBuf {
|
|
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
path.push("benches");
|
|
path.push("fixtures");
|
|
path.push("wasm");
|
|
path
|
|
}
|
|
|
|
fn any_fixture<F, M>(group: &mut BenchmarkGroup<M>, f: F)
|
|
where
|
|
F: Fn(Module),
|
|
M: Measurement,
|
|
{
|
|
for entry in read_dir(fixture_dir()).unwrap() {
|
|
let entry = entry.unwrap();
|
|
let bytes = read(entry.path()).unwrap();
|
|
group.throughput(Throughput::Bytes(bytes.len().try_into().unwrap()));
|
|
group.bench_with_input(entry.file_name().to_str().unwrap(), &bytes, |bench, input| {
|
|
bench.iter(|| f(deserialize_buffer(input).unwrap()))
|
|
});
|
|
}
|
|
}
|
|
|
|
fn gas_metering(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("Gas Metering");
|
|
any_fixture(&mut group, |module| {
|
|
gas_metering::inject(
|
|
module,
|
|
host_function::Injector::new("env", "gas"),
|
|
&ConstantCostRules::default(),
|
|
)
|
|
.unwrap();
|
|
});
|
|
}
|
|
|
|
fn stack_height_limiter(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("Stack Height Limiter");
|
|
any_fixture(&mut group, |module| {
|
|
inject_stack_limiter(module, 128).unwrap();
|
|
});
|
|
}
|
|
|
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
|
use wasmi::{
|
|
self,
|
|
core::{Pages, Value, F32},
|
|
Caller, Config, Engine, Extern, Func, Instance, Linker, Memory, StackLimits, Store,
|
|
};
|
|
fn prepare_module<P: Backend>(backend: P, input: &[u8]) -> (wasmi::Module, Store<u64>) {
|
|
let module = deserialize_buffer(input).unwrap();
|
|
let instrumented_module =
|
|
gas_metering::inject(module, backend, &ConstantCostRules::default()).unwrap();
|
|
let input = serialize(instrumented_module).unwrap();
|
|
// Prepare wasmi
|
|
let engine = Engine::new(&bench_config());
|
|
let module = wasmi::Module::new(&engine, &mut &input[..]).unwrap();
|
|
// Init host state with maximum gas_left
|
|
let store = Store::new(&engine, u64::MAX);
|
|
|
|
(module, store)
|
|
}
|
|
|
|
fn add_gas_host_func(linker: &mut Linker<u64>, store: &mut Store<u64>) {
|
|
// Create gas host function
|
|
let host_gas = Func::wrap(store, |mut caller: Caller<'_, u64>, param: u64| {
|
|
*caller.host_data_mut() -= param;
|
|
});
|
|
// Link the gas host function
|
|
linker.define("env", "gas", host_gas).unwrap();
|
|
}
|
|
|
|
fn add_gas_left_global(instance: &Instance, mut store: Store<u64>) -> Store<u64> {
|
|
instance
|
|
.get_export(&mut store, "gas_left")
|
|
.and_then(Extern::into_global)
|
|
.unwrap()
|
|
.set(&mut store, Value::I64(-1i64)) // the same as u64::MAX
|
|
.unwrap();
|
|
store
|
|
}
|
|
|
|
fn gas_metered_coremark(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("coremark, instrumented");
|
|
// Benchmark host_function::Injector
|
|
let wasm_filename = "coremark_minimal.wasm";
|
|
let bytes = read(fixture_dir().join(wasm_filename)).unwrap();
|
|
group.bench_function("with host_function::Injector", |bench| {
|
|
let backend = host_function::Injector::new("env", "gas");
|
|
let (module, mut store) = prepare_module(backend, &bytes);
|
|
// Link the host functions with the imported ones
|
|
let mut linker = <Linker<u64>>::new();
|
|
add_gas_host_func(&mut linker, &mut store);
|
|
// Create clock_ms host function.
|
|
let host_clock_ms = Func::wrap(&mut store, || {
|
|
SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u64
|
|
});
|
|
// Link the time measurer for the coremark wasm
|
|
linker.define("env", "clock_ms", host_clock_ms).unwrap();
|
|
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
|
|
bench.iter(|| {
|
|
let run = instance
|
|
.get_export(&mut store, "run")
|
|
.and_then(Extern::into_func)
|
|
.unwrap()
|
|
.typed::<(), F32>(&mut store)
|
|
.unwrap();
|
|
// Call the wasm!
|
|
run.call(&mut store, ()).unwrap();
|
|
})
|
|
});
|
|
|
|
group.bench_function("with mutable_global::Injector", |bench| {
|
|
let backend = mutable_global::Injector::new("gas_left");
|
|
let (module, mut store) = prepare_module(backend, &bytes);
|
|
// Add the gas_left mutable global
|
|
let mut linker = <Linker<u64>>::new();
|
|
// Create clock_ms host function.
|
|
let host_clock_ms = Func::wrap(&mut store, || {
|
|
SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u64
|
|
});
|
|
// Link the time measurer for the coremark wasm
|
|
linker.define("env", "clock_ms", host_clock_ms).unwrap();
|
|
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
let mut store = add_gas_left_global(&instance, store);
|
|
|
|
bench.iter(|| {
|
|
let run = instance
|
|
.get_export(&mut store, "run")
|
|
.and_then(Extern::into_func)
|
|
.unwrap()
|
|
.typed::<(), F32>(&mut store)
|
|
.unwrap();
|
|
// Call the wasm!
|
|
run.call(&mut store, ()).unwrap();
|
|
})
|
|
});
|
|
}
|
|
|
|
/// Converts the `.wat` encoded `bytes` into `.wasm` encoded bytes.
|
|
pub fn wat2wasm(bytes: &[u8]) -> Vec<u8> {
|
|
wat::parse_bytes(bytes).unwrap().into_owned()
|
|
}
|
|
|
|
/// Returns a [`Config`] useful for benchmarking.
|
|
fn bench_config() -> Config {
|
|
let mut config = Config::default();
|
|
config.set_stack_limits(StackLimits::new(1024, 1024 * 1024, 64 * 1024).unwrap());
|
|
config
|
|
}
|
|
|
|
fn gas_metered_recursive_ok(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("recursive_ok, instrumented");
|
|
const RECURSIVE_DEPTH: i32 = 8000;
|
|
let wasm_bytes = wat2wasm(include_bytes!("fixtures/wat/recursive_ok.wat"));
|
|
|
|
group.bench_function("with host_function::Injector", |bench| {
|
|
let backend = host_function::Injector::new("env", "gas");
|
|
let (module, mut store) = prepare_module(backend, &wasm_bytes);
|
|
// Link the host function with the imported one
|
|
let mut linker = <Linker<u64>>::new();
|
|
add_gas_host_func(&mut linker, &mut store);
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
|
|
let bench_call = instance.get_export(&store, "call").and_then(Extern::into_func).unwrap();
|
|
let mut result = [Value::I32(0)];
|
|
|
|
bench.iter(|| {
|
|
bench_call
|
|
.call(&mut store, &[Value::I32(RECURSIVE_DEPTH)], &mut result)
|
|
.unwrap();
|
|
assert_eq!(result, [Value::I32(0)]);
|
|
})
|
|
});
|
|
|
|
group.bench_function("with mutable_global::Injector", |bench| {
|
|
let backend = mutable_global::Injector::new("gas_left");
|
|
let (module, mut store) = prepare_module(backend, &wasm_bytes);
|
|
// Add the gas_left mutable global
|
|
let linker = <Linker<u64>>::new();
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
let mut store = add_gas_left_global(&instance, store);
|
|
|
|
let bench_call = instance.get_export(&store, "call").and_then(Extern::into_func).unwrap();
|
|
let mut result = [Value::I32(0)];
|
|
|
|
bench.iter(|| {
|
|
bench_call
|
|
.call(&mut store, &[Value::I32(RECURSIVE_DEPTH)], &mut result)
|
|
.unwrap();
|
|
assert_eq!(result, [Value::I32(0)]);
|
|
})
|
|
});
|
|
}
|
|
|
|
fn gas_metered_fibonacci_recursive(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("fibonacci_recursive, instrumented");
|
|
const FIBONACCI_REC_N: i64 = 10;
|
|
let wasm_bytes = wat2wasm(include_bytes!("fixtures/wat/fibonacci.wat"));
|
|
|
|
group.bench_function("with host_function::Injector", |bench| {
|
|
let backend = host_function::Injector::new("env", "gas");
|
|
let (module, mut store) = prepare_module(backend, &wasm_bytes);
|
|
// Link the host function with the imported one
|
|
let mut linker = <Linker<u64>>::new();
|
|
add_gas_host_func(&mut linker, &mut store);
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
|
|
let bench_call = instance
|
|
.get_export(&store, "fib_recursive")
|
|
.and_then(Extern::into_func)
|
|
.unwrap();
|
|
let mut result = [Value::I32(0)];
|
|
|
|
bench.iter(|| {
|
|
bench_call
|
|
.call(&mut store, &[Value::I64(FIBONACCI_REC_N)], &mut result)
|
|
.unwrap();
|
|
});
|
|
});
|
|
|
|
group.bench_function("with mutable_global::Injector", |bench| {
|
|
let backend = mutable_global::Injector::new("gas_left");
|
|
let (module, mut store) = prepare_module(backend, &wasm_bytes);
|
|
|
|
// Add the gas_left mutable global
|
|
let linker = <Linker<u64>>::new();
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
let mut store = add_gas_left_global(&instance, store);
|
|
|
|
let bench_call = instance
|
|
.get_export(&store, "fib_recursive")
|
|
.and_then(Extern::into_func)
|
|
.unwrap();
|
|
let mut result = [Value::I32(0)];
|
|
|
|
bench.iter(|| {
|
|
bench_call
|
|
.call(&mut store, &[Value::I64(FIBONACCI_REC_N)], &mut result)
|
|
.unwrap();
|
|
});
|
|
});
|
|
}
|
|
|
|
fn gas_metered_fac_recursive(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("factorial_recursive, instrumented");
|
|
let wasm_bytes = wat2wasm(include_bytes!("fixtures/wat/factorial.wat"));
|
|
|
|
group.bench_function("with host_function::Injector", |b| {
|
|
let backend = host_function::Injector::new("env", "gas");
|
|
let (module, mut store) = prepare_module(backend, &wasm_bytes);
|
|
// Link the host function with the imported one
|
|
let mut linker = <Linker<u64>>::new();
|
|
add_gas_host_func(&mut linker, &mut store);
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
let fac = instance
|
|
.get_export(&store, "recursive_factorial")
|
|
.and_then(Extern::into_func)
|
|
.unwrap();
|
|
let mut result = [Value::I64(0)];
|
|
|
|
b.iter(|| {
|
|
fac.call(&mut store, &[Value::I64(25)], &mut result).unwrap();
|
|
assert_eq!(result, [Value::I64(7034535277573963776)]);
|
|
})
|
|
});
|
|
|
|
group.bench_function("with mutable_global::Injector", |b| {
|
|
let backend = mutable_global::Injector::new("gas_left");
|
|
let (module, mut store) = prepare_module(backend, &wasm_bytes);
|
|
|
|
// Add the gas_left mutable global
|
|
let linker = <Linker<u64>>::new();
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
let mut store = add_gas_left_global(&instance, store);
|
|
let fac = instance
|
|
.get_export(&store, "recursive_factorial")
|
|
.and_then(Extern::into_func)
|
|
.unwrap();
|
|
let mut result = [Value::I64(0)];
|
|
|
|
b.iter(|| {
|
|
fac.call(&mut store, &[Value::I64(25)], &mut result).unwrap();
|
|
assert_eq!(result, [Value::I64(7034535277573963776)]);
|
|
})
|
|
});
|
|
}
|
|
|
|
fn gas_metered_count_until(c: &mut Criterion) {
|
|
const COUNT_UNTIL: i32 = 100_000;
|
|
let mut group = c.benchmark_group("count_until, instrumented");
|
|
let wasm_bytes = wat2wasm(include_bytes!("fixtures/wat/count_until.wat"));
|
|
|
|
group.bench_function("with host_function::Injector", |b| {
|
|
let backend = host_function::Injector::new("env", "gas");
|
|
let (module, mut store) = prepare_module(backend, &wasm_bytes);
|
|
// Link the host function with the imported one
|
|
let mut linker = <Linker<u64>>::new();
|
|
add_gas_host_func(&mut linker, &mut store);
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
let count_until =
|
|
instance.get_export(&store, "count_until").and_then(Extern::into_func).unwrap();
|
|
let mut result = [Value::I32(0)];
|
|
|
|
b.iter(|| {
|
|
count_until.call(&mut store, &[Value::I32(COUNT_UNTIL)], &mut result).unwrap();
|
|
assert_eq!(result, [Value::I32(COUNT_UNTIL)]);
|
|
})
|
|
});
|
|
|
|
group.bench_function("with mutable_global::Injector", |b| {
|
|
let backend = mutable_global::Injector::new("gas_left");
|
|
let (module, mut store) = prepare_module(backend, &wasm_bytes);
|
|
|
|
// Add the gas_left mutable global
|
|
let linker = <Linker<u64>>::new();
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
let mut store = add_gas_left_global(&instance, store);
|
|
let count_until =
|
|
instance.get_export(&store, "count_until").and_then(Extern::into_func).unwrap();
|
|
let mut result = [Value::I32(0)];
|
|
|
|
b.iter(|| {
|
|
count_until.call(&mut store, &[Value::I32(COUNT_UNTIL)], &mut result).unwrap();
|
|
assert_eq!(result, [Value::I32(COUNT_UNTIL)]);
|
|
})
|
|
});
|
|
}
|
|
|
|
fn gas_metered_vec_add(c: &mut Criterion) {
|
|
fn test_for<A, B>(
|
|
b: &mut Bencher,
|
|
vec_add: Func,
|
|
mut store: &mut Store<u64>,
|
|
mem: Memory,
|
|
len: usize,
|
|
vec_a: A,
|
|
vec_b: B,
|
|
) where
|
|
A: IntoIterator<Item = i32>,
|
|
B: IntoIterator<Item = i32>,
|
|
{
|
|
use core::mem::size_of;
|
|
|
|
let ptr_result = 10;
|
|
let len_result = len * size_of::<i64>();
|
|
let ptr_a = ptr_result + len_result;
|
|
let len_a = len * size_of::<i32>();
|
|
let ptr_b = ptr_a + len_a;
|
|
|
|
// Reset `result` buffer to zeros:
|
|
mem.data_mut(&mut store)[ptr_result..ptr_result + (len * size_of::<i32>())].fill(0);
|
|
// Initialize `a` buffer:
|
|
for (n, a) in vec_a.into_iter().take(len).enumerate() {
|
|
mem.write(&mut store, ptr_a + (n * size_of::<i32>()), &a.to_le_bytes()).unwrap();
|
|
}
|
|
// Initialize `b` buffer:
|
|
for (n, b) in vec_b.into_iter().take(len).enumerate() {
|
|
mem.write(&mut store, ptr_b + (n * size_of::<i32>()), &b.to_le_bytes()).unwrap();
|
|
}
|
|
|
|
// Prepare parameters and all Wasm `vec_add`:
|
|
let params = [
|
|
Value::I32(ptr_result as i32),
|
|
Value::I32(ptr_a as i32),
|
|
Value::I32(ptr_b as i32),
|
|
Value::I32(len as i32),
|
|
];
|
|
b.iter(|| {
|
|
vec_add.call(&mut store, ¶ms, &mut []).unwrap();
|
|
});
|
|
|
|
// Validate the result buffer:
|
|
for n in 0..len {
|
|
let mut buffer4 = [0x00; 4];
|
|
let mut buffer8 = [0x00; 8];
|
|
let a = {
|
|
mem.read(&store, ptr_a + (n * size_of::<i32>()), &mut buffer4).unwrap();
|
|
i32::from_le_bytes(buffer4)
|
|
};
|
|
let b = {
|
|
mem.read(&store, ptr_b + (n * size_of::<i32>()), &mut buffer4).unwrap();
|
|
i32::from_le_bytes(buffer4)
|
|
};
|
|
let actual_result = {
|
|
mem.read(&store, ptr_result + (n * size_of::<i64>()), &mut buffer8).unwrap();
|
|
i64::from_le_bytes(buffer8)
|
|
};
|
|
let expected_result = (a as i64) + (b as i64);
|
|
assert_eq!(
|
|
expected_result, actual_result,
|
|
"given a = {a} and b = {b}, results diverge at index {n}"
|
|
);
|
|
}
|
|
}
|
|
|
|
let mut group = c.benchmark_group("memory_vec_add, instrumented");
|
|
let wasm_bytes = wat2wasm(include_bytes!("fixtures/wat/memory-vec-add.wat"));
|
|
const LEN: usize = 100_000;
|
|
|
|
group.bench_function("with host_function::Injector", |b| {
|
|
let backend = host_function::Injector::new("env", "gas");
|
|
let (module, mut store) = prepare_module(backend, &wasm_bytes);
|
|
// Link the host function with the imported one
|
|
let mut linker = <Linker<u64>>::new();
|
|
add_gas_host_func(&mut linker, &mut store);
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
let vec_add = instance.get_export(&store, "vec_add").and_then(Extern::into_func).unwrap();
|
|
let mem = instance.get_export(&store, "mem").and_then(Extern::into_memory).unwrap();
|
|
mem.grow(&mut store, Pages::new(25).unwrap()).unwrap();
|
|
test_for(
|
|
b,
|
|
vec_add,
|
|
&mut store,
|
|
mem,
|
|
LEN,
|
|
(0..LEN).map(|i| (i * i) as i32),
|
|
(0..LEN).map(|i| (i * 10) as i32),
|
|
)
|
|
});
|
|
|
|
group.bench_function("with mutable_global::Injector", |b| {
|
|
let backend = mutable_global::Injector::new("gas_left");
|
|
let (module, mut store) = prepare_module(backend, &wasm_bytes);
|
|
// Add the gas_left mutable global
|
|
let linker = <Linker<u64>>::new();
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
let mut store = add_gas_left_global(&instance, store);
|
|
let vec_add = instance.get_export(&store, "vec_add").and_then(Extern::into_func).unwrap();
|
|
let mem = instance.get_export(&store, "mem").and_then(Extern::into_memory).unwrap();
|
|
mem.grow(&mut store, Pages::new(25).unwrap()).unwrap();
|
|
test_for(
|
|
b,
|
|
vec_add,
|
|
&mut store,
|
|
mem,
|
|
LEN,
|
|
(0..LEN).map(|i| (i * i) as i32),
|
|
(0..LEN).map(|i| (i * 10) as i32),
|
|
)
|
|
});
|
|
}
|
|
|
|
fn gas_metered_tiny_keccak(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("wasm_kernel::tiny_keccak, instrumented");
|
|
let wasm_filename = "wasm_kernel.wasm";
|
|
let wasm_bytes = read(fixture_dir().join(wasm_filename)).unwrap();
|
|
|
|
group.bench_function("with host_function::Injector", |b| {
|
|
let backend = host_function::Injector::new("env", "gas");
|
|
let (module, mut store) = prepare_module(backend, &wasm_bytes);
|
|
// Link the host function with the imported one
|
|
let mut linker = <Linker<u64>>::new();
|
|
add_gas_host_func(&mut linker, &mut store);
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
let prepare = instance
|
|
.get_export(&store, "prepare_tiny_keccak")
|
|
.and_then(Extern::into_func)
|
|
.unwrap();
|
|
let keccak = instance
|
|
.get_export(&store, "bench_tiny_keccak")
|
|
.and_then(Extern::into_func)
|
|
.unwrap();
|
|
let mut test_data_ptr = Value::I32(0);
|
|
prepare.call(&mut store, &[], slice::from_mut(&mut test_data_ptr)).unwrap();
|
|
b.iter(|| {
|
|
keccak.call(&mut store, slice::from_ref(&test_data_ptr), &mut []).unwrap();
|
|
})
|
|
});
|
|
|
|
group.bench_function("with mutable_global::Injector", |b| {
|
|
let backend = mutable_global::Injector::new("gas_left");
|
|
let (module, mut store) = prepare_module(backend, &wasm_bytes);
|
|
// Add the gas_left mutable global
|
|
let linker = <Linker<u64>>::new();
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
let mut store = add_gas_left_global(&instance, store);
|
|
let prepare = instance
|
|
.get_export(&store, "prepare_tiny_keccak")
|
|
.and_then(Extern::into_func)
|
|
.unwrap();
|
|
let keccak = instance
|
|
.get_export(&store, "bench_tiny_keccak")
|
|
.and_then(Extern::into_func)
|
|
.unwrap();
|
|
let mut test_data_ptr = Value::I32(0);
|
|
prepare.call(&mut store, &[], slice::from_mut(&mut test_data_ptr)).unwrap();
|
|
b.iter(|| {
|
|
keccak.call(&mut store, slice::from_ref(&test_data_ptr), &mut []).unwrap();
|
|
})
|
|
});
|
|
}
|
|
|
|
fn gas_metered_global_bump(c: &mut Criterion) {
|
|
const BUMP_AMOUNT: i32 = 100_000;
|
|
let mut group = c.benchmark_group("global_bump, instrumented");
|
|
let wasm_bytes = wat2wasm(include_bytes!("fixtures/wat/global_bump.wat"));
|
|
|
|
group.bench_function("with host_function::Injector", |b| {
|
|
let backend = host_function::Injector::new("env", "gas");
|
|
let (module, mut store) = prepare_module(backend, &wasm_bytes);
|
|
// Link the host function with the imported one
|
|
let mut linker = <Linker<u64>>::new();
|
|
add_gas_host_func(&mut linker, &mut store);
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
let bump = instance.get_export(&store, "bump").and_then(Extern::into_func).unwrap();
|
|
let mut result = [Value::I32(0)];
|
|
|
|
b.iter(|| {
|
|
bump.call(&mut store, &[Value::I32(BUMP_AMOUNT)], &mut result).unwrap();
|
|
assert_eq!(result, [Value::I32(BUMP_AMOUNT)]);
|
|
})
|
|
});
|
|
|
|
group.bench_function("with mutable_global::Injector", |b| {
|
|
let backend = mutable_global::Injector::new("gas_left");
|
|
let (module, mut store) = prepare_module(backend, &wasm_bytes);
|
|
// Add the gas_left mutable global
|
|
let linker = <Linker<u64>>::new();
|
|
let instance = linker.instantiate(&mut store, &module).unwrap().start(&mut store).unwrap();
|
|
let mut store = add_gas_left_global(&instance, store);
|
|
let bump = instance.get_export(&store, "bump").and_then(Extern::into_func).unwrap();
|
|
let mut result = [Value::I32(0)];
|
|
|
|
b.iter(|| {
|
|
bump.call(&mut store, &[Value::I32(BUMP_AMOUNT)], &mut result).unwrap();
|
|
assert_eq!(result, [Value::I32(BUMP_AMOUNT)]);
|
|
})
|
|
});
|
|
}
|
|
|
|
criterion_group!(benches, gas_metering, stack_height_limiter);
|
|
criterion_group!(
|
|
name = coremark;
|
|
config = Criterion::default()
|
|
.sample_size(10)
|
|
.measurement_time(Duration::from_millis(275000))
|
|
.warm_up_time(Duration::from_millis(1000));
|
|
targets =
|
|
gas_metered_coremark,
|
|
);
|
|
criterion_group!(
|
|
name = wasmi_fixtures;
|
|
config = Criterion::default()
|
|
.sample_size(10)
|
|
.measurement_time(Duration::from_millis(250000))
|
|
.warm_up_time(Duration::from_millis(1000));
|
|
targets =
|
|
gas_metered_recursive_ok,
|
|
gas_metered_fibonacci_recursive,
|
|
gas_metered_fac_recursive,
|
|
gas_metered_count_until,
|
|
gas_metered_vec_add,
|
|
gas_metered_tiny_keccak,
|
|
gas_metered_global_bump,
|
|
);
|
|
criterion_main!(coremark, wasmi_fixtures);
|