fix(bench): run warmup benchmark to break JIT bias (#19844)

Closes https://github.com/denoland/deno/issues/15277

This commit adds a single "warmup" run of empty function when running
`deno bench`.
This change will break so-called "JIT bias" which makes V8 optimize the
first function
and then bail out of optimization on second function. In essence the
"warmup" function
is getting optimized and then all user benches are bailed out of
optimization.
This commit is contained in:
Bartek Iwańczuk 2023-07-17 23:17:28 +02:00 committed by GitHub
parent 4ebe3bdb06
commit 298e414936
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 34 additions and 0 deletions

View file

@ -685,6 +685,8 @@ function test(
});
}
let registeredWarmupBench = false;
// Main bench function provided by Deno.
function bench(
nameOrFnOrOptions,
@ -695,6 +697,25 @@ function bench(
return;
}
if (!registeredWarmupBench) {
registeredWarmupBench = true;
const warmupBenchDesc = {
name: "<warmup>",
fn: function warmup() {},
async: false,
ignore: false,
baseline: false,
only: false,
sanitizeExit: true,
permissions: null,
warmup: true,
};
warmupBenchDesc.fn = wrapBenchmark(warmupBenchDesc);
const { id, origin } = ops.op_register_bench(warmupBenchDesc);
warmupBenchDesc.id = id;
warmupBenchDesc.origin = origin;
}
let benchDesc;
const defaults = {
ignore: false,
@ -777,6 +798,7 @@ function bench(
const AsyncFunction = (async () => {}).constructor;
benchDesc.async = AsyncFunction === benchDesc.fn.constructor;
benchDesc.fn = wrapBenchmark(benchDesc);
benchDesc.warmup = false;
const { id, origin } = ops.op_register_bench(benchDesc);
benchDesc.id = id;

View file

@ -101,6 +101,8 @@ struct BenchInfo<'s> {
group: Option<String>,
ignore: bool,
only: bool,
#[serde(default)]
warmup: bool,
}
#[derive(Debug, Serialize)]
@ -128,6 +130,7 @@ fn op_register_bench<'a>(
group: info.group,
ignore: info.ignore,
only: info.only,
warmup: info.warmup,
};
let function: v8::Local<v8::Function> = info.function.v8_value.try_into()?;
let function = v8::Global::new(scope, function);

View file

@ -95,6 +95,7 @@ pub struct BenchDescription {
pub group: Option<String>,
pub ignore: bool,
pub only: bool,
pub warmup: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -194,6 +195,10 @@ impl BenchReporter for JsonReporter {
fn report_output(&mut self, _output: &str) {}
fn report_result(&mut self, desc: &BenchDescription, result: &BenchResult) {
if desc.warmup {
return;
}
let maybe_bench = self.0.benches.iter_mut().find(|bench| {
bench.origin == desc.origin
&& bench.group == desc.group
@ -326,6 +331,10 @@ impl BenchReporter for ConsoleReporter {
}
fn report_result(&mut self, desc: &BenchDescription, result: &BenchResult) {
if desc.warmup {
return;
}
let options = self.options.as_ref().unwrap();
match result {