From c71fb4a005d4b1681dbcb253d5ccef7d77b20f43 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/Cargo.toml | 1 + wasmtime-basic/README.md | 10 ++++- wasmtime-basic/src/main.rs | 82 +++++++++++++++++++++++++++++++++----- 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/wasmtime-basic/Cargo.toml b/wasmtime-basic/Cargo.toml index b35ad0a..48d510f 100644 --- a/wasmtime-basic/Cargo.toml +++ b/wasmtime-basic/Cargo.toml @@ -20,3 +20,4 @@ default = ["rust"] c = [] rust = [] +benchmark = [] diff --git a/wasmtime-basic/README.md b/wasmtime-basic/README.md index 75331d2..8c2b941 100644 --- a/wasmtime-basic/README.md +++ b/wasmtime-basic/README.md @@ -18,4 +18,12 @@ Rust is compiled by default. If C is desired, the appropriate feature can be inv cargo 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 +## 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. Additionally, the `benchmark` feature must be enabled to run them: + +``` +cargo +nightly bench --features benchmark +``` \ No newline at end of file diff --git a/wasmtime-basic/src/main.rs b/wasmtime-basic/src/main.rs index 40cfe25..fac6569 100644 --- a/wasmtime-basic/src/main.rs +++ b/wasmtime-basic/src/main.rs @@ -2,12 +2,18 @@ //! Rust-powered JIT, to natively run programs from several different source //! languages (Rust/C/C++) compiled to WASI-compliant WASM. +#![cfg_attr(feature = "benchmark", feature(test))] + +#[cfg(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 +24,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 +57,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), + context.invoke(&mut instance, "add", &args) +} + +// A Rust function that does the same thing as the WASM demo, for benchmarking use. +#[cfg(test)] +pub fn native_add(a: i32, b: i32) -> i32 { + a + b +} + +// 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, 7)); + } + + // 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."); }