Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 41 additions & 32 deletions benches/benches/scripts.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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");

Expand All @@ -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<PreparedScriptBench> = 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));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should put the initialization code inside the benchmark, it'll just pollute the results.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was about to say. The reason we use the main function is to benchmark specific bits of VM, not the initialization and parsing (and optimization, etc).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I was mainly trying to avoid the eager init issue but putting it inside the benchmark isnt the right tradeoff here... thinking of instead filtering before registration so only matching scripts get initialized, and keeping the setup outside bench_function like before

does that sound like the right direction?

let function = prepared.function.clone();
let context = &mut prepared.context;

group.bench_function("Execution", |b| {
b.iter(|| function.call(&JsValue::undefined(), &[], context));
});
group.finish();
Expand Down
Loading