From afe206ffc7277c2af25900721f5bbc9002091f04 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Mon, 21 Nov 2022 11:38:55 -0800 Subject: [PATCH] bench: add more WASI benchmarks This follows up on #5274 to add several more scenarios with which to benchmark WASI performance: - `open-file.wat`: opens and closes a file - `read-file.wat`: opens a file, reads 4K bytes from it, then closes it - `read-dir.wat`: reads a directory's entries Each benchmark is hand-crafted WAT to more clearly control what WASI calls are made. As with #5274, these modules' sole entry point takes a parameter indicating the number of iterations to run in order to use `criterion`'s `iter_custom` feature. --- benches/wasi.rs | 34 ++++++++++++++++- benches/wasi/.gitignore | 1 + benches/wasi/open-file.wat | 53 ++++++++++++++++++++++++++ benches/wasi/read-dir.wat | 40 +++++++++++++++++++ benches/wasi/read-file.wat | 78 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 benches/wasi/.gitignore create mode 100644 benches/wasi/open-file.wat create mode 100644 benches/wasi/read-dir.wat create mode 100644 benches/wasi/read-file.wat diff --git a/benches/wasi.rs b/benches/wasi.rs index 3b8f28863abd..bb28a2f449df 100644 --- a/benches/wasi.rs +++ b/benches/wasi.rs @@ -1,7 +1,11 @@ //! Measure some common WASI call scenarios. use criterion::{criterion_group, criterion_main, Criterion}; -use std::time::Instant; +use std::{ + fs::File, + path::{Path, PathBuf}, + time::Instant, +}; use wasmtime::{Engine, Linker, Module, Store, TypedFunc}; use wasmtime_wasi::{sync::WasiCtxBuilder, WasiCtx}; @@ -9,6 +13,13 @@ criterion_group!(benches, bench_wasi); criterion_main!(benches); fn bench_wasi(c: &mut Criterion) { + let _ = env_logger::try_init(); + + let test_file = Path::new("benches/wasi/test.bin"); + if !test_file.is_file() { + fill_file_with_zeroes(test_file, 4096); + } + // Benchmark each `*.wat` file in the `wasi` directory. for file in std::fs::read_dir("benches/wasi").unwrap() { let path = file.unwrap().path(); @@ -67,5 +78,26 @@ fn wasi_context() -> WasiCtx { "--flag4".to_string(), ]) .unwrap() + .preopened_dir( + wasmtime_wasi::Dir::open_ambient_dir( + get_fixture_directory(), + wasmtime_wasi::ambient_authority(), + ) + .unwrap(), + "/fixtures", + ) + .unwrap() .build() } + +fn get_fixture_directory() -> PathBuf { + let mut dir = PathBuf::from(file!()); + dir.pop(); + dir.push("wasi"); + dir +} + +fn fill_file_with_zeroes(path: &Path, num_bytes: usize) { + let file = File::create(path).unwrap(); + file.set_len(num_bytes.try_into().unwrap()).unwrap() +} diff --git a/benches/wasi/.gitignore b/benches/wasi/.gitignore new file mode 100644 index 000000000000..a0c61d0c3ae4 --- /dev/null +++ b/benches/wasi/.gitignore @@ -0,0 +1 @@ +test.bin diff --git a/benches/wasi/open-file.wat b/benches/wasi/open-file.wat new file mode 100644 index 000000000000..390562ee81c4 --- /dev/null +++ b/benches/wasi/open-file.wat @@ -0,0 +1,53 @@ +;; Repeatedly open and close `test.bin`. +(module + (import "wasi_snapshot_preview1" "path_open" + (func $__wasi_path_open (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "fd_read" + (func $__wasi_fd_read (param i32 i32 i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "fd_close" + (func $__wasi_fd_close (param i32) (result i32))) + (func (export "run") (param $iters i64) (result i64) + (local $i i64) + (local.set $i (i64.const 0)) + (loop $cont + ;; Open the file `test.bin` under the same directory as this WAT + ;; file; this assumes some prior set up of the preopens in + ;; `wasi.rs`. See https://github.com/WebAssembly/WASI/blob/d8da230b/phases/snapshot/witx/wasi_snapshot_preview1.witx#L346. + (call $__wasi_path_open + ;; The fd of the preopen under which to search for the file; + ;; the first three are the `std*` ones. + (i32.const 3) + ;; The lookup flags (i.e., whether to follow symlinks). + (i32.const 0) + ;; The path to the file under the initial fd. + (i32.const 0) + (i32.const 8) + ;; The open flags; in this case we will only attempt to read but + ;; this may attempt to create the file if it does not exist, see + ;; https://github.com/WebAssembly/WASI/blob/d8da230b/phases/snapshot/witxtypenames.witx#L444). + (i32.const 0) + ;; The base rights and the inheriting rights: here we only set + ;; the bits for the FD_READ and FD_READDIR capabilities. + (i64.const 0x2002) + (i64.const 0x2002) + ;; The file descriptor flags (e.g., whether to append, sync, + ;; etc.); see https://github.com/WebAssembly/WASI/blob/d8da230b/phases/snapshot/witx/typenames.witx#L385 + (i32.const 0) + ;; The address at which to store the opened fd (if the call + ;; succeeds) + (i32.const 16)) + (if (then unreachable)) + + ;; Close the open file handle we stored at offset 16. + (call $__wasi_fd_close (i32.load (i32.const 16))) + (if (then unreachable)) + + ;; 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) + ) + (data (i32.const 0) "test.bin") + (memory (export "memory") 1) +) diff --git a/benches/wasi/read-dir.wat b/benches/wasi/read-dir.wat new file mode 100644 index 000000000000..c90c4b1b7661 --- /dev/null +++ b/benches/wasi/read-dir.wat @@ -0,0 +1,40 @@ +;; Read the directory entries of the preopened directory. +(module + (import "wasi_snapshot_preview1" "fd_readdir" + (func $__wasi_fd_readdir (param i32 i32 i32 i64 i32) (result i32))) + (func (export "run") (param $iters i64) (result i64) + (local $i i64) + (local.set $i (i64.const 0)) + + (if (i32.ne (i32.load (i32.const 0)) (i32.const 0)) + (then unreachable)) + + (loop $cont + ;; Read the file into the sole iovec buffer. + (call $__wasi_fd_readdir + ;; The fd of the preopened directory; the first three are the + ;; `std*` ones. + (i32.const 3) + ;; The buffer address at which to store the entries and the + ;; length of the buffer. + (i32.const 16) + (i32.const 4096) + ;; The location at which to start reading entries in the + ;; directory; here we start at the first entry. + (i64.const 0) + ;; The address at which to store the number of bytes read. + (i32.const 8)) + (drop) + + ;; Check that we indeed read 380 bytes of directory entries. + (if (i32.lt_u (i32.load (i32.const 8)) (i32.const 380)) + (then unreachable)) + + ;; 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) +) diff --git a/benches/wasi/read-file.wat b/benches/wasi/read-file.wat new file mode 100644 index 000000000000..9e3d2bb5be2b --- /dev/null +++ b/benches/wasi/read-file.wat @@ -0,0 +1,78 @@ +;; Repeatedly read the contents of `test.bin`. +(module + (import "wasi_snapshot_preview1" "path_open" + (func $__wasi_path_open (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "fd_read" + (func $__wasi_fd_read (param i32 i32 i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "fd_close" + (func $__wasi_fd_close (param i32) (result i32))) + (func (export "run") (param $iters i64) (result i64) + (local $i i64) + (local.set $i (i64.const 0)) + + ;; Set up the iovec list; the memory usage for this module should be: + ;; - offset 0 => file name + ;; - offset 16 => the opened file descriptor + ;; - offset 24 => the number of read bytes + ;; - offset 32 => the iovec list + ;; - offset 48 => the first (and only) iovec buffer + (i32.store (i32.const 32) (i32.const 48)) + (i32.store (i32.const 36) (i32.const 4096)) + + (loop $cont + ;; Open the file `test.bin` under the same directory as this WAT + ;; file; this assumes some prior set up of the preopens in + ;; `wasi.rs`. See https://github.com/WebAssembly/WASI/blob/d8da230b/phases/snapshot/witx/wasi_snapshot_preview1.witx#L346. + (call $__wasi_path_open + ;; The fd of the preopen under which to search for the file; + ;; the first three are the `std*` ones. + (i32.const 3) + ;; The lookup flags (i.e., whether to follow symlinks). + (i32.const 0) + ;; The path to the file under the initial fd. + (i32.const 0) + (i32.const 8) + ;; The open flags; in this case we will only attempt to read but + ;; this may attempt to create the file if it does not exist, see + ;; https://github.com/WebAssembly/WASI/blob/d8da230b/phases/snapshot/witxtypenames.witx#L444). + (i32.const 0) + ;; The base rights and the inheriting rights: here we only set + ;; the bits for the FD_READ and FD_READDIR capabilities. + (i64.const 0x2002) + (i64.const 0x2002) + ;; The file descriptor flags (e.g., whether to append, sync, + ;; etc.); see https://github.com/WebAssembly/WASI/blob/d8da230b/phases/snapshot/witx/typenames.witx#L385 + (i32.const 0) + ;; The address at which to store the opened fd (if the call + ;; succeeds) + (i32.const 16)) + (if (then unreachable)) + + ;; Read the file into the sole iovec buffer. + (call $__wasi_fd_read + ;; The now-open fd stored at offset 16. + (i32.load (i32.const 16)) + ;; The address and size of the list of iovecs; here we only use + ;; a list of a single iovec set up outside the loop. + (i32.const 32) + (i32.const 1) + ;; The address at which to store the number of bytes read. + (i32.const 24)) + (if (then unreachable)) + ;; Check that we indeed read 4096 bytes. + (if (i32.ne (i32.load (i32.const 24)) (i32.const 4096)) + (then unreachable)) + + ;; Close the open file handle we stored at offset 16. + (call $__wasi_fd_close (i32.load (i32.const 16))) + (if (then unreachable)) + + ;; 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) + ) + (data (i32.const 0) "test.bin") + (memory (export "memory") 1) +)