From a8aa10b4fab3bd767b48201b4219114ab50140ea Mon Sep 17 00:00:00 2001 From: Mark Bestavros Date: Wed, 4 Sep 2019 16:03:13 -0400 Subject: [PATCH] Implement performance benchmarking for basic demo --- wasmtime-basic/README.md | 16 ++++++-- wasmtime-basic/src/main.rs | 79 ++++++++++++++++++++++++++++++++++---- 2 files changed, 84 insertions(+), 11 deletions(-) diff --git a/wasmtime-basic/README.md b/wasmtime-basic/README.md index 75331d2..967ac3b 100644 --- a/wasmtime-basic/README.md +++ b/wasmtime-basic/README.md @@ -4,7 +4,7 @@ This is an example crate demonstrating how Enarx may use Wasmtime, a Rust-powere ## Running the demo -By default, the Rust demo should run with `cargo run` out of the box. +The demo requires Rust nightly. If that's set up, the Rust demo should run with `cargo +nightly run` out of the box. If you wish to compile the C demo, you'll need to install `lld` . On Fedora, this can be accomplished with `sudo dnf install lld`. @@ -15,7 +15,17 @@ The WASM binary used for the demo can be compiled from either Rust or C. Compila Rust is compiled by default. If C is desired, the appropriate feature can be invoked via the `cargo` command: ``` -cargo run --no-default-features --features c +cargo +nightly run --no-default-features --features c ``` -Or, alternatively, by changing the `default` feature in `Cargo.toml` from `["rust"]` to `["c"]`. \ No newline at end of file +Or, alternatively, by changing the `default` feature in `Cargo.toml` from `["rust"]` to `["c"]`. + +## Running benchmarks + +`main.rs` includes performance benchmarks useful for analyzing the startup cost and runtime of functions in Wasmtime. + +These benchmarks use Rust's unstable built-in benchmarking tools, and must be run on Rust nightly: + +``` +cargo +nightly bench +``` \ No newline at end of file diff --git a/wasmtime-basic/src/main.rs b/wasmtime-basic/src/main.rs index 40cfe25..50cea85 100644 --- a/wasmtime-basic/src/main.rs +++ b/wasmtime-basic/src/main.rs @@ -2,12 +2,17 @@ //! Rust-powered JIT, to natively run programs from several different source //! languages (Rust/C/C++) compiled to WASI-compliant WASM. +#![feature(test)] + +extern crate test; + use cranelift_codegen::settings; use cranelift_native; use std::fs::File; use std::io::Read; -use wasmtime_jit::{ActionOutcome, Context, RuntimeValue}; +use wasmtime_jit::{ActionError, ActionOutcome, Context, RuntimeValue}; +// The basic WASM demo itself. fn main() { // Before we begin, it'll be heplful to know which source language we're // running, so let's print it out. @@ -18,11 +23,22 @@ fn main() { println!("WASM binary has been compiled from Rust."); } - println!("Loading WASM binary..."); + println!("Loading and running WASM binary..."); + let result = wasm_add_full(); + println!("Finished. Results:"); + match result.unwrap() { + ActionOutcome::Returned { values } => println!("Output: {:#?}", values), + ActionOutcome::Trapped { message } => println!("Trap from within function: {}", message), + } + println!("Done."); +} + +// A function to encapsulate the full setup and execution of a single iteration of the WASM demo. +pub fn wasm_add_full() -> Result { + // First, we need to load the WASM binary in. let mut binary_file = File::open(concat!(env!("OUT_DIR"), "/add.wasm")).unwrap(); let mut binary: Vec = Vec::new(); binary_file.read_to_end(&mut binary).unwrap(); - println!("WASM binary loaded."); // In order to run this binary, we need to prepare a few inputs. @@ -40,12 +56,59 @@ fn main() { // And, finally, invoke our function and print the results. // For this demo, all we're doing is adding 5 and 7 together. - println!("Invoking function."); let args = [RuntimeValue::I32(5), RuntimeValue::I32(7)]; let result = context.invoke(&mut instance, "add", &args); - match result.unwrap() { - ActionOutcome::Returned { values } => println!("Output: {:#?}", values), - ActionOutcome::Trapped { message } => println!("Trap from within function: {}", message), + return result; +} + +// A Rust function that does the same thing as the WASM demo, for benchmarking use. +pub fn native_add(a: i32) -> i32 { + a + 7 +} + +// Performance benchmarking for the demo. +#[cfg(test)] +mod tests { + use super::*; + use test::Bencher; + + // Baseline benchmark: Simple Rust adition. + #[bench] + fn bench_native_add(b: &mut Bencher) { + b.iter(|| native_add(5)); + } + + // Benchmark: WASM addition from start to finish, including + // loading/instantiating WASM. + #[bench] + fn bench_wasm_add_unloaded(b: &mut Bencher) { + b.iter(|| wasm_add_full()); + } + + // Benchmark: WASM addition once everything is set up. + #[bench] + fn bench_wasm_add_loaded(b: &mut Bencher) { + let mut binary_file = File::open(concat!(env!("OUT_DIR"), "/add.wasm")).unwrap(); + let mut binary: Vec = Vec::new(); + binary_file.read_to_end(&mut binary).unwrap(); + + // In order to run this binary, we need to prepare a few inputs. + + // First, we need a Wasmtime context. To build one, we need to get an ISA + // from `cranelift_native. + let isa_builder = cranelift_native::builder().unwrap(); + let flag_builder = settings::builder(); + let isa = isa_builder.finish(settings::Flags::new(flag_builder)); + + // Then, we use the ISA to build the context. + let mut context = Context::with_isa(isa); + + // Now, we instantiate the WASM module loaded into memory. + let mut instance = context.instantiate_module(None, &binary).unwrap(); + + // And, finally, invoke our function and print the results. + // For this demo, all we're doing is adding 5 and 7 together. + let args = [RuntimeValue::I32(5), RuntimeValue::I32(7)]; + b.iter(|| context.invoke(&mut instance, "add", &args)); } - println!("Done."); }