Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bench: benchmark several common WASI scenarios #5274

Merged
merged 3 commits into from
Nov 16, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,7 @@ harness = false
[[bench]]
name = "call"
harness = false

[[bench]]
name = "wasi"
harness = false
2 changes: 1 addition & 1 deletion benches/thread_eager_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::time::{Duration, Instant};
use wasmtime::*;

fn measure_execution_time(c: &mut Criterion) {
// Baseline performance: a single measurment covers both initializing
// Baseline performance: a single measurement covers both initializing
// thread local resources and executing the first call.
//
// The other two bench functions should sum to this duration.
Expand Down
73 changes: 73 additions & 0 deletions benches/wasi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//! Measure some common WASI call scenarios.

use criterion::{criterion_group, criterion_main, Criterion};
use std::time::Instant;
use wasmtime::{Engine, Linker, Module, Store, TypedFunc};
use wasmtime_wasi::{sync::WasiCtxBuilder, WasiCtx};

criterion_group!(benches, bench_wasi);
criterion_main!(benches);

fn bench_wasi(c: &mut Criterion) {
// Benchmark each `*.wat` file in the `wasi` directory.
for file in std::fs::read_dir("benches/wasi").unwrap() {
let path = file.unwrap().path();
if path.extension().map(|e| e == "wat").unwrap_or(false) {
let wat = std::fs::read(&path).unwrap();
let (mut store, run_fn) = instantiate(&wat);
let bench_name = format!("wasi/{}", path.file_name().unwrap().to_string_lossy());
// To avoid overhead, the module itself must iterate the expected
// number of times in a specially-crafted `run` function (see
// `instantiate` for deta\ils).
abrown marked this conversation as resolved.
Show resolved Hide resolved
c.bench_function(&bench_name, move |b| {
b.iter_custom(|iters| {
let start = Instant::now();
let result = run_fn.call(&mut store, iters).unwrap();
assert_eq!(iters, result);
start.elapsed()
})
});
}
}
}

/// Compile and instantiate the Wasm module, returning the exported `run`
/// function. This function expects `run` to:
/// - have a single `u64` parameter indicating the number of loop iterations to
/// execute
/// - execute the body of the function for that number of loop iterations
/// - return a single `u64` indicating how many loop iterations were executed
/// (to double-check)
fn instantiate(wat: &[u8]) -> (Store<WasiCtx>, TypedFunc<u64, u64>) {
let engine = Engine::default();
let wasi = wasi_context();
let mut store = Store::new(&engine, wasi);
let module = Module::new(&engine, wat).unwrap();
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |cx| cx).unwrap();
let instance = linker.instantiate(&mut store, &module).unwrap();
let run = instance
.get_typed_func::<_, _, _>(&mut store, "run")
abrown marked this conversation as resolved.
Show resolved Hide resolved
.unwrap();
(store, run)
}

/// Build a WASI context with some actual data to retrieve.
fn wasi_context() -> WasiCtx {
let wasi = WasiCtxBuilder::new();
wasi.envs(&[
("a".to_string(), "b".to_string()),
("b".to_string(), "c".to_string()),
("c".to_string(), "d".to_string()),
])
.unwrap()
.args(&[
"exe".to_string(),
"--flag1".to_string(),
"--flag2".to_string(),
"--flag3".to_string(),
"--flag4".to_string(),
])
.unwrap()
.build()
}
22 changes: 22 additions & 0 deletions benches/wasi/get-current-time.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
(module
(import "wasi_snapshot_preview1" "clock_time_get"
(func $__wasi_clock_time_get (param i32 i64 i32) (result i32)))
(func (export "run") (param $iters i64) (result i64)
(local $i i64)
(local.set $i (i64.const 0))
(loop $cont
;; Retrieve the current time with the following parameters:
;; - $clockid: here we use the enum value for $realtime
;; - $precision: the maximum lag, which we set to 0 here
;; - the address at which to write the u64 $timestamp
;; Returns an error code.
(call $__wasi_clock_time_get (i32.const 1) (i64.const 0) (i32.const 0))
(drop)
;; Continue looping until $i reaches $iters.
(local.set $i (i64.add (local.get $i) (i64.const 1)))
(br_if $cont (i64.lt_u (local.get $i) (local.get $iters)))
)
(local.get $i)
)
(memory (export "memory") 1)
)
42 changes: 42 additions & 0 deletions benches/wasi/read-arguments.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
(module
(import "wasi_snapshot_preview1" "args_get"
(func $__wasi_args_get (param i32 i32) (result i32)))
(import "wasi_snapshot_preview1" "args_sizes_get"
(func $__wasi_args_sizes_get (param i32 i32) (result i32)))
(func (export "run") (param $iters i64) (result i64)
(local $i i64)
(local.set $i (i64.const 0))
(loop $cont
;; Read the current argument list by:
;; 1) retrieving the argument sizes and then
;; 2) retrieving the argument data itself.

;; Retrieve the sizes of the arguments with parameters:
;; - the address at which to write the number of arguments
;; - the address at which to write the size of the argument buffer
;; Returns an error code.
(call $__wasi_args_sizes_get (i32.const 0) (i32.const 4))
(drop)

;; Read the arguments with parameters:
;; - the address at which to write the array of argument pointers
;; (i.e., one pointer per argument); here we overwrite the size
;; written at address 0
;; - the address at which to write the buffer of argument strings
;; (pointed to by the items written to the first address); we
;; calculate where to start the buffer based on the size of the
;; pointer list (i.e., number of arguments * 4 bytes per pointer)
;; Returns an error code.
(call $__wasi_args_get
(i32.const 0)
(i32.mul (i32.load (i32.const 0)) (i32.const 4)))
(drop)

;; Continue looping until $i reaches $iters.
(local.set $i (i64.add (local.get $i) (i64.const 1)))
(br_if $cont (i64.lt_u (local.get $i) (local.get $iters)))
)
(local.get $i)
)
(memory (export "memory") 1)
)
45 changes: 45 additions & 0 deletions benches/wasi/read-environment.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
(module
(import "wasi_snapshot_preview1" "environ_get"
(func $__wasi_environ_get (param i32 i32) (result i32)))
(import "wasi_snapshot_preview1" "environ_sizes_get"
(func $__wasi_environ_sizes_get (param i32 i32) (result i32)))
(func (export "run") (param $iters i64) (result i64)
(local $i i64)
(local.set $i (i64.const 0))
(loop $cont
;; Read the current environment key-value pairs by:
;; 1) retrieving the environment sizes and then
;; 2) retrieving the environment data itself.

;; Retrieve the sizes of the environment with parameters:
;; - the address at which to write the number of environment
;; variables
;; - the address at which to write the size of the environment
;; buffer
;; Returns an error code.
(call $__wasi_environ_sizes_get (i32.const 0) (i32.const 4))
(drop)

;; Read the environment with parameters:
;; - the address at which to write the array of environment pointers
;; (i.e., one pointer per key-value pair); here we overwrite
;; the size written at address 0
;; - the address at which to write the buffer of key-value pairs
;; (pointed to by the items written to the first address); we
;; calculate where to start the buffer based on the size of the
;; pointer list (i.e., number of key-value pairs * 4 bytes per
;; pointer)
;; Returns an error code.
(call $__wasi_environ_get
(i32.const 0)
(i32.mul (i32.load (i32.const 0)) (i32.const 4)))
(drop)

;; Continue looping until $i reaches $iters.
(local.set $i (i64.add (local.get $i) (i64.const 1)))
(br_if $cont (i64.lt_u (local.get $i) (local.get $iters)))
)
(local.get $i)
)
(memory (export "memory") 1)
)