diff --git a/Cargo.lock b/Cargo.lock index d3bf569b42..0b1a28142b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,13 +64,14 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.7" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ - "getrandom", + "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -82,6 +83,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "aml" version = "0.16.4" @@ -110,6 +117,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstream" version = "0.6.7" @@ -456,6 +469,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.0.83" @@ -687,6 +706,42 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + [[package]] name = "crossbeam-channel" version = "0.5.11" @@ -696,6 +751,25 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.19" @@ -1228,15 +1302,16 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "heck" @@ -1513,7 +1588,7 @@ dependencies = [ name = "key_value_lookup" version = "0.1.0" dependencies = [ - "hashbrown 0.12.3", + "hashbrown 0.14.3", "http", "log", "maplit", @@ -2152,7 +2227,7 @@ name = "oak_echo_service" version = "0.1.0" dependencies = [ "async-trait", - "hashbrown 0.12.3", + "hashbrown 0.14.3", "log", "micro_rpc", "micro_rpc_build", @@ -2264,7 +2339,7 @@ dependencies = [ "command-fds", "env_logger", "futures", - "hashbrown 0.12.3", + "hashbrown 0.14.3", "log", "micro_rpc", "micro_rpc_build", @@ -2305,7 +2380,7 @@ dependencies = [ name = "oak_functions_sdk" version = "0.1.0" dependencies = [ - "hashbrown 0.12.3", + "hashbrown 0.14.3", "lazy_static", "micro_rpc", "micro_rpc_build", @@ -2342,8 +2417,9 @@ dependencies = [ "anyhow", "byteorder", "bytes", + "criterion", "env_logger", - "hashbrown 0.12.3", + "hashbrown 0.14.3", "log", "micro_rpc", "micro_rpc_build", @@ -2413,7 +2489,7 @@ dependencies = [ "bmrng", "clap", "command-fds", - "hashbrown 0.12.3", + "hashbrown 0.14.3", "log", "micro_rpc", "micro_rpc_build", @@ -2529,7 +2605,7 @@ name = "oak_restricted_kernel_sdk_proc_macro" version = "0.1.0" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] @@ -2648,6 +2724,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -2884,6 +2966,34 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -3120,6 +3230,26 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "rayon" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -3862,6 +3992,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tokio" version = "1.35.1" @@ -4356,6 +4496,16 @@ dependencies = [ "xtask", ] +[[package]] +name = "web-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "which" version = "4.4.2" diff --git a/oak_functions_service/Cargo.toml b/oak_functions_service/Cargo.toml index c5125d1eb1..9319e989a0 100644 --- a/oak_functions_service/Cargo.toml +++ b/oak_functions_service/Cargo.toml @@ -11,6 +11,10 @@ default = ["deny_sensitive_logging"] deny_sensitive_logging = [] std = ["anyhow/std", "wasmi/std"] +[[bench]] +name = "wasm_benchmark" +harness = false + [dependencies] anyhow = { version = "*", default-features = false } byteorder = { version = "*", default-features = false } @@ -34,6 +38,7 @@ wasmi = { version = "*", default-features = false } micro_rpc_build = { workspace = true } [dev-dependencies] +criterion = "*" env_logger = { version = "*", default-features = false } oak_attestation = { workspace = true } oak_functions_test_utils = { workspace = true } diff --git a/oak_functions_service/benches/wasm_benchmark.rs b/oak_functions_service/benches/wasm_benchmark.rs new file mode 100644 index 0000000000..800d9993df --- /dev/null +++ b/oak_functions_service/benches/wasm_benchmark.rs @@ -0,0 +1,103 @@ +// +// Copyright 2024 The Project Oak Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +use std::sync::Arc; + +use criterion::{criterion_group, criterion_main, Criterion}; +use hashbrown::HashMap; +use oak_functions_abi::Request; +use oak_functions_service::{ + logger::StandaloneLogger, + lookup::{Data, LookupDataManager}, + wasm::WasmHandler, +}; + +fn bench_invoke_echo(c: &mut Criterion) { + let test_state = create_test_state_with("echo"); + let data = b"Hello, world!"; + + c.bench_function("echo", |b| { + b.iter(|| { + let response = test_state + .wasm_handler + .handle_invoke(Request { + body: data.to_vec(), + }) + .unwrap(); + assert_eq!(response.body, data.to_vec()); + }) + }); +} + +fn bench_invoke_lookup(c: &mut Criterion) { + let test_state = create_test_state_with("key_value_lookup"); + let request = b"key10"; + let expected_response = b"value10"; + + test_state + .lookup_data_manager + .extend_next_lookup_data(create_test_data(0, 1000)); + test_state.lookup_data_manager.finish_next_lookup_data(); + + c.bench_function("lookup", |b| { + b.iter(|| { + let response = test_state + .wasm_handler + .handle_invoke(Request { + body: request.to_vec(), + }) + .unwrap(); + assert_eq!(response.body, expected_response); + }) + }); +} + +fn create_test_data(start: i32, end: i32) -> Data { + HashMap::from_iter((start..end).map(|i| { + ( + format!("key{}", i).into_bytes().into(), + format!("value{}", i).into_bytes().into(), + ) + })) +} + +struct TestState { + wasm_handler: WasmHandler, + lookup_data_manager: Arc>, +} + +fn create_test_state_with(wasm_module_name: &str) -> TestState { + let logger = StandaloneLogger {}; + let lookup_data_manager = Arc::new(LookupDataManager::for_test(HashMap::new(), logger.clone())); + let wasm_module_path = + oak_functions_test_utils::build_rust_crate_wasm(wasm_module_name).unwrap(); + let wasm_module_bytes = std::fs::read(wasm_module_path).unwrap(); + + let wasm_handler = oak_functions_service::wasm::new_wasm_handler( + &wasm_module_bytes, + lookup_data_manager.clone(), + None, + ) + .unwrap(); + + TestState { + wasm_handler, + lookup_data_manager, + } +} + +criterion_group!(benches, bench_invoke_echo, bench_invoke_lookup); +criterion_main!(benches); diff --git a/oak_functions_service/src/wasm/tests.rs b/oak_functions_service/src/wasm/tests.rs index 4d7ea5a83e..8449d071b0 100644 --- a/oak_functions_service/src/wasm/tests.rs +++ b/oak_functions_service/src/wasm/tests.rs @@ -17,13 +17,11 @@ extern crate test; use alloc::{sync::Arc, vec::Vec}; -use std::time::Duration; use byteorder::{ByteOrder, LittleEndian}; use hashbrown::HashMap; use oak_functions_abi::Request; use spinning_top::Spinlock; -use test::Bencher; use super::{ api::StdWasmApiFactory, OakLinker, UserState, WasmApiFactory, WasmHandler, ALLOC_FUNCTION_NAME, @@ -143,40 +141,6 @@ fn test_invoke() { assert_eq!(response.body, data.to_vec()); } -#[bench] -fn bench_invoke(bencher: &mut Bencher) { - let test_state = create_test_state(); - let data = b"Hello, world!"; - - let summary = bencher.bench(|bencher| { - bencher.iter(|| { - let response = test_state - .wasm_handler - .handle_invoke(Request { - body: data.to_vec(), - }) - .unwrap(); - assert_eq!(response.body, data.to_vec()); - }); - Ok(()) - }); - - // When running `cargo test` this benchmark test gets executed too, but `summary` will be `None` - // in that case. So, here we first check that `summary` is not empty. - if let Ok(Some(summary)) = summary { - // `summary.mean` is in nanoseconds, even though it is not explicitly documented in - // https://doc.rust-lang.org/test/stats/struct.Summary.html. - let elapsed = Duration::from_nanos(summary.mean as u64); - // We expect the `mean` time for loading the test Wasm module and running its main function - // to be less than a fixed threshold. - assert!( - elapsed < Duration::from_micros(100), - "elapsed time: {:.0?}", - elapsed - ); - } -} - struct TestState { instance: wasmi::Instance, store: wasmi::Store>, @@ -188,7 +152,7 @@ fn create_test_state() -> TestState { let logger = StandaloneLogger {}; let lookup_data_manager = Arc::new(LookupDataManager::for_test(HashMap::new(), logger.clone())); let api_factory = Arc::new(StdWasmApiFactory { - lookup_data_manager, + lookup_data_manager: lookup_data_manager.clone(), }); let wasm_module_path = oak_functions_test_utils::build_rust_crate_wasm("echo").unwrap();