diff options
Diffstat (limited to 'bench_util/src')
-rw-r--r-- | bench_util/src/js_runtime.rs | 89 | ||||
-rw-r--r-- | bench_util/src/lib.rs | 6 | ||||
-rw-r--r-- | bench_util/src/profiling.rs | 81 |
3 files changed, 176 insertions, 0 deletions
diff --git a/bench_util/src/js_runtime.rs b/bench_util/src/js_runtime.rs new file mode 100644 index 000000000..701849b5a --- /dev/null +++ b/bench_util/src/js_runtime.rs @@ -0,0 +1,89 @@ +use bencher::Bencher; +use deno_core::v8; +use deno_core::JsRuntime; + +use crate::profiling::is_profiling; + +pub fn create_js_runtime(setup: impl FnOnce(&mut JsRuntime)) -> JsRuntime { + let mut rt = JsRuntime::new(Default::default()); + + // Setup bootstrap namespace + rt.execute("bootstrap", "globalThis.__bootstrap = {};") + .unwrap(); + + // Caller provided setup + setup(&mut rt); + + // Init ops + rt.execute( + "init", + r#" + Deno.core.ops(); + Deno.core.registerErrorClass('Error', Error); + "#, + ) + .unwrap(); + + rt +} + +fn loop_code(iters: u64, src: &str) -> String { + format!(r#"for(let i=0; i < {}; i++) {{ {} }}"#, iters, src,) +} + +pub fn bench_js_sync( + b: &mut Bencher, + src: &str, + setup: impl FnOnce(&mut JsRuntime), +) { + let mut runtime = create_js_runtime(setup); + let context = runtime.global_context(); + let scope = &mut v8::HandleScope::with_context(runtime.v8_isolate(), context); + + // Increase JS iterations if profiling for nicer flamegraphs + let inner_iters = 1000 * if is_profiling() { 10000 } else { 1 }; + // Looped code + let looped_src = loop_code(inner_iters, src); + + let code = v8::String::new(scope, looped_src.as_ref()).unwrap(); + let script = v8::Script::compile(scope, code, None).unwrap(); + + // Run once if profiling, otherwise regular bench loop + if is_profiling() { + script.run(scope).unwrap(); + } else { + b.iter(|| { + script.run(scope).unwrap(); + }); + } +} + +pub fn bench_js_async( + b: &mut Bencher, + src: &str, + setup: impl FnOnce(&mut JsRuntime), +) { + let mut runtime = create_js_runtime(setup); + let tokio_runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + + // Looped code + let looped = loop_code(1000, src); + let src = looped.as_ref(); + + if is_profiling() { + for _ in 0..10000 { + runtime.execute("inner_loop", src).unwrap(); + let future = runtime.run_event_loop(); + tokio_runtime.block_on(future).unwrap(); + } + } else { + b.iter(|| { + runtime.execute("inner_loop", src).unwrap(); + let future = runtime.run_event_loop(); + tokio_runtime.block_on(future).unwrap(); + }); + } +} diff --git a/bench_util/src/lib.rs b/bench_util/src/lib.rs new file mode 100644 index 000000000..41aaf4004 --- /dev/null +++ b/bench_util/src/lib.rs @@ -0,0 +1,6 @@ +mod js_runtime; +mod profiling; + +pub use bencher; +pub use js_runtime::*; +pub use profiling::*; // Exports bench_or_profile! macro diff --git a/bench_util/src/profiling.rs b/bench_util/src/profiling.rs new file mode 100644 index 000000000..498c70b9c --- /dev/null +++ b/bench_util/src/profiling.rs @@ -0,0 +1,81 @@ +use bencher::{DynBenchFn, StaticBenchFn, TestDescAndFn, TestOpts}; + +pub fn is_profiling() -> bool { + std::env::var("PROFILING").is_ok() +} + +#[macro_export] +// Tweaked and copied from https://github.com/bluss/bencher/blob/master/macros.rs +macro_rules! bench_or_profile { + ($($group_name:path),+) => { + fn main() { + use $crate::bencher::TestOpts; + use $crate::bencher::run_tests_console; + let mut test_opts = TestOpts::default(); + // check to see if we should filter: + if let Some(arg) = ::std::env::args().skip(1).find(|arg| *arg != "--bench") { + test_opts.filter = Some(arg); + } + let mut benches = Vec::new(); + $( + benches.extend($group_name()); + )+ + + if $crate::is_profiling() { + // Run profling + $crate::run_profiles(&test_opts, benches); + } else { + // Run benches + run_tests_console(&test_opts, benches).unwrap(); + } + } + }; + ($($group_name:path,)+) => { + bench_or_profile!($($group_name),+); + }; +} + +pub fn run_profiles(opts: &TestOpts, tests: Vec<TestDescAndFn>) { + let tests = filter_tests(opts, tests); + // let decs = tests.iter().map(|t| t.desc.clone()).collect(); + + println!(); + for b in tests { + println!("Profiling {}", b.desc.name); + run_profile(b); + } + println!(); +} + +fn run_profile(test: TestDescAndFn) { + match test.testfn { + DynBenchFn(bencher) => { + bencher::bench::run_once(|harness| bencher.run(harness)); + } + StaticBenchFn(benchfn) => { + bencher::bench::run_once(|harness| benchfn(harness)); + } + }; +} + +// Copied from https://github.com/bluss/bencher/blob/master/lib.rs +fn filter_tests( + opts: &TestOpts, + tests: Vec<TestDescAndFn>, +) -> Vec<TestDescAndFn> { + let mut filtered = tests; + + // Remove tests that don't match the test filter + filtered = match opts.filter { + None => filtered, + Some(ref filter) => filtered + .into_iter() + .filter(|test| test.desc.name.contains(&filter[..])) + .collect(), + }; + + // Sort the tests alphabetically + filtered.sort_by(|t1, t2| t1.desc.name.cmp(&t2.desc.name)); + + filtered +} |