From d0ad0f85cbc50414e7bd2541bfc81aca319cb000 Mon Sep 17 00:00:00 2001 From: Zaid <161572905+iammdzaidalam@users.noreply.github.com> Date: Sat, 21 Mar 2026 03:48:45 +0530 Subject: [PATCH 1/2] bench: lazily initialize script benchmarks --- benches/benches/scripts.rs | 73 +++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/benches/benches/scripts.rs b/benches/benches/scripts.rs index b185966773c..0d20e3fc952 100644 --- a/benches/benches/scripts.rs +++ b/benches/benches/scripts.rs @@ -1,6 +1,7 @@ #![allow(unused_crate_dependencies, missing_docs)] use boa_engine::{ - Context, JsValue, Source, js_string, optimizer::OptimizerOptions, script::Script, + Context, JsValue, Source, js_string, object::JsObject, optimizer::OptimizerOptions, + script::Script, }; use criterion::{Criterion, criterion_group, criterion_main}; use std::{path::Path, time::Duration}; @@ -9,6 +10,39 @@ use std::{path::Path, time::Duration}; #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; +struct PreparedScriptBench { + context: Context, + function: JsObject, +} + +fn prepare_script_bench(path: &Path) -> PreparedScriptBench { + let code = std::fs::read_to_string(path).unwrap(); + + let mut context = Context::default(); + context.set_optimizer_options(OptimizerOptions::empty()); + + boa_runtime::register( + boa_runtime::extensions::ConsoleExtension(boa_runtime::NullLogger), + None, + &mut context, + ) + .expect("Runtime registration failed"); + + let script = Script::parse(Source::from_bytes(&code), None, &mut context).unwrap(); + script.codeblock(&mut context).unwrap(); + script.evaluate(&mut context).unwrap(); + + let function = context + .global_object() + .get(js_string!("main"), &mut context) + .unwrap_or_else(|_| panic!("No main function defined in script: {}", path.display())) + .as_callable() + .unwrap_or_else(|| panic!("'main' is not a function in script: {}", path.display())) + .clone(); + + PreparedScriptBench { context, function } +} + fn bench_scripts(c: &mut Criterion) { let scripts_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("scripts"); @@ -26,49 +60,24 @@ fn bench_scripts(c: &mut Criterion) { for entry in scripts { let path = entry.path(); - let code = std::fs::read_to_string(path).unwrap(); - // Create a nice benchmark name from the relative path let rel_path = path.strip_prefix(&scripts_dir).unwrap().with_extension(""); let name = rel_path.display().to_string(); let mut group = c.benchmark_group(&name); - // Use reduced sample size for slow benchmarks (e.g., v8-benches) if rel_path.starts_with("v8-benches") { group.sample_size(10); group.measurement_time(Duration::from_secs(5)); } - let context = &mut Context::default(); - - // Disable optimizations - context.set_optimizer_options(OptimizerOptions::empty()); - - // Register runtime for console.log support - boa_runtime::register( - boa_runtime::extensions::ConsoleExtension(boa_runtime::NullLogger), - None, - context, - ) - .expect("Runtime registration failed"); - - // Parse and compile once, outside the benchmark loop - let script = Script::parse(Source::from_bytes(&code), None, context).unwrap(); - script.codeblock(context).unwrap(); - - // Evaluate once to define the main function - script.evaluate(context).unwrap(); + let path = path.to_path_buf(); + let mut prepared: Option = None; - // Get the main function - let function = context - .global_object() - .get(js_string!("main"), context) - .unwrap_or_else(|_| panic!("No main function defined in script: {}", path.display())) - .as_callable() - .unwrap_or_else(|| panic!("'main' is not a function in script: {}", path.display())) - .clone(); + group.bench_function("Execution", move |b| { + let prepared = prepared.get_or_insert_with(|| prepare_script_bench(&path)); + let function = prepared.function.clone(); + let context = &mut prepared.context; - group.bench_function("Execution", |b| { b.iter(|| function.call(&JsValue::undefined(), &[], context)); }); group.finish(); From 61ed8cd59199c6963babc2d1810e818d7672725c Mon Sep 17 00:00:00 2001 From: Zaid <161572905+iammdzaidalam@users.noreply.github.com> Date: Fri, 10 Apr 2026 19:47:30 +0530 Subject: [PATCH 2/2] fix(benches): defer benchmark initialization properly and refine loop allocations --- Cargo.lock | 1 + benches/Cargo.toml | 1 + benches/benches/scripts.rs | 27 +++++++++++++++++++++------ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec963893ed5..c64685a4244 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -345,6 +345,7 @@ dependencies = [ "boa_runtime", "criterion", "jemallocator", + "regex", "walkdir", ] diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 4be3c394e14..18a9b486783 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -11,6 +11,7 @@ boa_runtime.workspace = true [dev-dependencies] criterion.workspace = true walkdir = "2" +regex.workspace = true [target.x86_64-unknown-linux-gnu.dev-dependencies] jemallocator.workspace = true diff --git a/benches/benches/scripts.rs b/benches/benches/scripts.rs index 0d20e3fc952..111579e3408 100644 --- a/benches/benches/scripts.rs +++ b/benches/benches/scripts.rs @@ -44,6 +44,11 @@ fn prepare_script_bench(path: &Path) -> PreparedScriptBench { } fn bench_scripts(c: &mut Criterion) { + let args: Vec = std::env::args().collect(); + let is_list = args.iter().any(|arg| arg == "--list"); + let filter = args.iter().skip(1).find(|arg| !arg.starts_with('-')); + let filter_re = filter.and_then(|f| regex::Regex::new(f).ok()); + let scripts_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("scripts"); let scripts: Vec<_> = walkdir::WalkDir::new(&scripts_dir) @@ -70,15 +75,25 @@ fn bench_scripts(c: &mut Criterion) { group.measurement_time(Duration::from_secs(5)); } - let path = path.to_path_buf(); - let mut prepared: Option = None; + let should_run = !is_list + && filter.is_none_or(|f| { + let full_name = format!("{name}/Execution"); + if let Some(re) = &filter_re { + re.is_match(&full_name) + } else { + full_name.contains(f) + } + }); + + let mut prepared = should_run.then(|| prepare_script_bench(path)); group.bench_function("Execution", move |b| { - let prepared = prepared.get_or_insert_with(|| prepare_script_bench(&path)); - let function = prepared.function.clone(); - let context = &mut prepared.context; + if let Some(prepared) = prepared.as_mut() { + let function = prepared.function.clone(); + let context = &mut prepared.context; - b.iter(|| function.call(&JsValue::undefined(), &[], context)); + b.iter(|| function.call(&JsValue::undefined(), &[], context)); + } }); group.finish(); }