mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
tooling(bench_util): benching and profiling utilities (#10223)
This commit is contained in:
parent
8fb1af1412
commit
733a000305
8 changed files with 220 additions and 65 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -1,5 +1,7 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "Inflector"
|
name = "Inflector"
|
||||||
version = "0.11.4"
|
version = "0.11.4"
|
||||||
|
@ -194,6 +196,15 @@ version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bench_util"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"bencher",
|
||||||
|
"deno_core",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bencher"
|
name = "bencher"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
@ -580,7 +591,7 @@ name = "deno_core"
|
||||||
version = "0.84.0"
|
version = "0.84.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bencher",
|
"bench_util",
|
||||||
"futures",
|
"futures",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
|
|
@ -7,6 +7,7 @@ members = [
|
||||||
"runtime",
|
"runtime",
|
||||||
"serde_v8",
|
"serde_v8",
|
||||||
"test_plugin",
|
"test_plugin",
|
||||||
|
"bench_util",
|
||||||
"test_util",
|
"test_util",
|
||||||
"op_crates/crypto",
|
"op_crates/crypto",
|
||||||
"op_crates/fetch",
|
"op_crates/fetch",
|
||||||
|
|
16
bench_util/Cargo.toml
Normal file
16
bench_util/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "bench_util"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["the Deno authors"]
|
||||||
|
edition = "2018"
|
||||||
|
description = "Bench and profiling utilities for deno crates"
|
||||||
|
license = "MIT"
|
||||||
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/denoland/deno"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bencher = "0.1"
|
||||||
|
deno_core = { version = "0.84.0", path = "../core" }
|
||||||
|
tokio = { version = "1.4.0", features = ["full"] }
|
89
bench_util/src/js_runtime.rs
Normal file
89
bench_util/src/js_runtime.rs
Normal file
|
@ -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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
6
bench_util/src/lib.rs
Normal file
6
bench_util/src/lib.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
mod js_runtime;
|
||||||
|
mod profiling;
|
||||||
|
|
||||||
|
pub use bencher;
|
||||||
|
pub use js_runtime::*;
|
||||||
|
pub use profiling::*; // Exports bench_or_profile! macro
|
81
bench_util/src/profiling.rs
Normal file
81
bench_util/src/profiling.rs
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ path = "examples/http_bench_json_ops.rs"
|
||||||
# These dependencies are only used for the 'http_bench_*_ops' examples.
|
# These dependencies are only used for the 'http_bench_*_ops' examples.
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "1.4.0", features = ["full"] }
|
tokio = { version = "1.4.0", features = ["full"] }
|
||||||
bencher = "0.1"
|
bench_util = { version = "0.0.0", path = "../bench_util" }
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "op_baseline"
|
name = "op_baseline"
|
||||||
|
|
|
@ -1,38 +1,25 @@
|
||||||
use bencher::{benchmark_group, benchmark_main, Bencher};
|
|
||||||
|
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::op_async;
|
use deno_core::op_async;
|
||||||
use deno_core::op_sync;
|
use deno_core::op_sync;
|
||||||
use deno_core::serialize_op_result;
|
use deno_core::serialize_op_result;
|
||||||
use deno_core::v8;
|
|
||||||
use deno_core::JsRuntime;
|
use deno_core::JsRuntime;
|
||||||
use deno_core::Op;
|
use deno_core::Op;
|
||||||
use deno_core::OpState;
|
use deno_core::OpState;
|
||||||
use deno_core::ZeroCopyBuf;
|
use deno_core::ZeroCopyBuf;
|
||||||
|
|
||||||
|
use bench_util::bench_or_profile;
|
||||||
|
use bench_util::bencher::{benchmark_group, Bencher};
|
||||||
|
use bench_util::{bench_js_async, bench_js_sync};
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
fn create_js_runtime() -> JsRuntime {
|
fn setup(rt: &mut JsRuntime) {
|
||||||
let mut runtime = JsRuntime::new(Default::default());
|
rt.register_op("pi_json", op_sync(|_, _: (), _| Ok(314159)));
|
||||||
runtime.register_op("pi_json", op_sync(|_, _: (), _| Ok(314159)));
|
rt.register_op("pi_async", op_async(op_pi_async));
|
||||||
runtime.register_op("pi_async", op_async(op_pi_async));
|
rt.register_op("nop", |state, _, _| {
|
||||||
runtime.register_op("nop", |state, _, _| {
|
|
||||||
Op::Sync(serialize_op_result(Ok(9), state))
|
Op::Sync(serialize_op_result(Ok(9), state))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Init ops
|
|
||||||
runtime
|
|
||||||
.execute(
|
|
||||||
"init",
|
|
||||||
r#"
|
|
||||||
Deno.core.ops();
|
|
||||||
Deno.core.registerErrorClass('Error', Error);
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
runtime
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is a function since async closures aren't stable
|
// this is a function since async closures aren't stable
|
||||||
|
@ -44,57 +31,21 @@ async fn op_pi_async(
|
||||||
Ok(314159)
|
Ok(314159)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bench_runtime_js(b: &mut Bencher, src: &str) {
|
|
||||||
let mut runtime = create_js_runtime();
|
|
||||||
let context = runtime.global_context();
|
|
||||||
let scope = &mut v8::HandleScope::with_context(runtime.v8_isolate(), context);
|
|
||||||
let code = v8::String::new(scope, src).unwrap();
|
|
||||||
let script = v8::Script::compile(scope, code, None).unwrap();
|
|
||||||
b.iter(|| {
|
|
||||||
script.run(scope).unwrap();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bench_runtime_js_async(b: &mut Bencher, src: &str) {
|
|
||||||
let mut runtime = create_js_runtime();
|
|
||||||
let tokio_runtime = tokio::runtime::Builder::new_current_thread()
|
|
||||||
.enable_all()
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
b.iter(|| {
|
|
||||||
runtime.execute("inner_loop", src).unwrap();
|
|
||||||
let future = runtime.run_event_loop();
|
|
||||||
tokio_runtime.block_on(future).unwrap();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_op_pi_json(b: &mut Bencher) {
|
fn bench_op_pi_json(b: &mut Bencher) {
|
||||||
bench_runtime_js(
|
bench_js_sync(b, r#"Deno.core.opSync("pi_json");"#, setup);
|
||||||
b,
|
|
||||||
r#"for(let i=0; i < 1e3; i++) {
|
|
||||||
Deno.core.opSync("pi_json", null);
|
|
||||||
}"#,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench_op_nop(b: &mut Bencher) {
|
fn bench_op_nop(b: &mut Bencher) {
|
||||||
bench_runtime_js(
|
bench_js_sync(
|
||||||
b,
|
b,
|
||||||
r#"for(let i=0; i < 1e3; i++) {
|
r#"Deno.core.dispatchByName("nop", null, null, null);"#,
|
||||||
Deno.core.dispatchByName("nop", null, null, null);
|
setup,
|
||||||
}"#,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench_op_async(b: &mut Bencher) {
|
fn bench_op_async(b: &mut Bencher) {
|
||||||
bench_runtime_js_async(
|
bench_js_async(b, r#"Deno.core.opAsync("pi_async");"#, setup);
|
||||||
b,
|
|
||||||
r#"for(let i=0; i < 1e3; i++) {
|
|
||||||
Deno.core.opAsync("pi_async", null);
|
|
||||||
}"#,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
benchmark_group!(benches, bench_op_pi_json, bench_op_nop, bench_op_async);
|
benchmark_group!(benches, bench_op_pi_json, bench_op_nop, bench_op_async);
|
||||||
benchmark_main!(benches);
|
bench_or_profile!(benches);
|
||||||
|
|
Loading…
Reference in a new issue