From e6f56563bc84516e017b3db06f11fab2549b9a50 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 23 Aug 2021 13:15:57 -0400 Subject: [PATCH] feat: tests and benchmarks --- packages/core/.vscode/settings.json | 2 +- packages/core/Cargo.toml | 5 + packages/core/benches/create.rs | 8 +- packages/core/benches/jsframework.rs | 131 ++++++++++++++++++++++++++ packages/core/examples/jsframework.rs | 109 +++++++++++++++++++++ packages/core/src/diff.rs | 6 +- 6 files changed, 251 insertions(+), 10 deletions(-) create mode 100644 packages/core/benches/jsframework.rs create mode 100644 packages/core/examples/jsframework.rs diff --git a/packages/core/.vscode/settings.json b/packages/core/.vscode/settings.json index 79505b018..1c68f97cc 100644 --- a/packages/core/.vscode/settings.json +++ b/packages/core/.vscode/settings.json @@ -1,3 +1,3 @@ { - "rust-analyzer.inlayHints.enable": false + "rust-analyzer.inlayHints.enable": true } diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 264a6c56d..005afdd29 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -49,6 +49,7 @@ async-std = { version = "1.9.0", features = ["attributes"] } criterion = "0.3.5" dioxus-html = { path = "../html" } fern = { version = "0.6.0", features = ["colored"] } +rand = { version = "0.8.4", features = ["small_rng"] } simple_logger = "1.13.0" @@ -59,3 +60,7 @@ serialize = ["serde"] [[bench]] name = "create" harness = false + +[[bench]] +name = "jsframework" +harness = false diff --git a/packages/core/benches/create.rs b/packages/core/benches/create.rs index 2b0c26ef5..2f614ac0e 100644 --- a/packages/core/benches/create.rs +++ b/packages/core/benches/create.rs @@ -13,10 +13,4 @@ fn criterion_benchmark(c: &mut Criterion) { } criterion_group!(benches, criterion_benchmark); - -fn main() { - benches(); - Criterion::default().configure_from_args().final_summary(); - // $crate::__warn_about_html_reports_feature(); - // $crate::__warn_about_cargo_bench_support_feature(); -} +criterion_main!(benches); diff --git a/packages/core/benches/jsframework.rs b/packages/core/benches/jsframework.rs new file mode 100644 index 000000000..91b97dd74 --- /dev/null +++ b/packages/core/benches/jsframework.rs @@ -0,0 +1,131 @@ +//! This benchmark tests just the overhead of Dioxus itself. +//! +//! For the JS Framework Benchmark, both the framework and the browser is benchmarked together. Dioxus prepares changes +//! to be made, but the change application phase will be just as performant as the vanilla wasm_bindgen code. In essence, +//! we are measuring the overhead of Dioxus, not the performance of the "apply" phase. +//! +//! On my MBP 2019: +//! - Dioxus takes 3ms to create 1_000 rows +//! - Dioxus takes 30ms to create 10_000 rows +//! +//! As pure "overhead", these are really really good numbers, mostly slowed down by hitting the global allocator. +//! These numbers don't represent Dioxus with the heuristic engine installed, so I assume it'll be even faster. + +use std::fmt::Display; + +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use dioxus_core as dioxus; +use dioxus_core::prelude::*; +use dioxus_html as dioxus_elements; +use rand::prelude::*; + +criterion_group!(mbenches, create_rows); +criterion_main!(mbenches); + +fn create_rows(c: &mut Criterion) { + static App: FC<()> = |cx| { + let mut rng = SmallRng::from_entropy(); + let rows = (0..10_000).map(|f| { + let label = Label::new(&mut rng); + rsx! { + Row { + row_id: f, + label: label + } + } + }); + cx.render(rsx! { + table { + tbody { + {rows} + } + } + }) + }; + + c.bench_function("create rows", |b| { + b.iter(|| { + let mut dom = VirtualDom::new(App); + let g = dom.rebuild().unwrap(); + assert!(g.edits.len() > 1); + }) + }); +} + +#[derive(PartialEq, Props)] +struct RowProps { + row_id: usize, + label: Label, +} +fn Row<'a>(cx: Context<'a, RowProps>) -> DomTree { + cx.render(rsx! { + tr { + td { class:"col-md-1", "{cx.row_id}" } + td { class:"col-md-1", onclick: move |_| { /* run onselect */ } + a { class: "lbl", "{cx.label}" } + } + td { class: "col-md-1" + a { class: "remove", onclick: move |_| {/* remove */} + span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" } + } + } + td { class: "col-md-6" } + } + }) +} + +#[derive(PartialEq)] +struct Label([&'static str; 3]); + +impl Label { + fn new(rng: &mut SmallRng) -> Self { + Label([ + ADJECTIVES.choose(rng).unwrap(), + COLOURS.choose(rng).unwrap(), + NOUNS.choose(rng).unwrap(), + ]) + } +} +impl Display for Label { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {} {}", self.0[0], self.0[1], self.0[2]) + } +} + +static ADJECTIVES: &[&str] = &[ + "pretty", + "large", + "big", + "small", + "tall", + "short", + "long", + "handsome", + "plain", + "quaint", + "clean", + "elegant", + "easy", + "angry", + "crazy", + "helpful", + "mushy", + "odd", + "unsightly", + "adorable", + "important", + "inexpensive", + "cheap", + "expensive", + "fancy", +]; + +static COLOURS: &[&str] = &[ + "red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", + "orange", +]; + +static NOUNS: &[&str] = &[ + "table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", + "pizza", "mouse", "keyboard", +]; diff --git a/packages/core/examples/jsframework.rs b/packages/core/examples/jsframework.rs new file mode 100644 index 000000000..84be02832 --- /dev/null +++ b/packages/core/examples/jsframework.rs @@ -0,0 +1,109 @@ +use dioxus_core as dioxus; +use dioxus_core::prelude::*; +use dioxus_html as dioxus_elements; +use rand::prelude::*; +use std::fmt::Display; + +fn main() { + let mut dom = VirtualDom::new(App); + let g = dom.rebuild().unwrap(); + assert!(g.edits.len() > 1); +} + +static App: FC<()> = |cx| { + let mut rng = SmallRng::from_entropy(); + let rows = (0..10_000).map(|f| { + let label = Label::new(&mut rng); + rsx! { + Row { + row_id: f, + label: label + } + } + }); + cx.render(rsx! { + table { + tbody { + {rows} + } + } + }) +}; + +#[derive(PartialEq, Props)] +struct RowProps { + row_id: usize, + label: Label, +} +fn Row<'a>(cx: Context<'a, RowProps>) -> DomTree { + cx.render(rsx! { + tr { + td { class:"col-md-1", "{cx.row_id}" } + td { class:"col-md-1", onclick: move |_| { /* run onselect */ } + a { class: "lbl", "{cx.label}" } + } + td { class: "col-md-1" + a { class: "remove", onclick: move |_| {/* remove */} + span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" } + } + } + td { class: "col-md-6" } + } + }) +} + +#[derive(PartialEq)] +struct Label([&'static str; 3]); + +impl Label { + fn new(rng: &mut SmallRng) -> Self { + Label([ + ADJECTIVES.choose(rng).unwrap(), + COLOURS.choose(rng).unwrap(), + NOUNS.choose(rng).unwrap(), + ]) + } +} +impl Display for Label { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {} {}", self.0[0], self.0[1], self.0[2]) + } +} + +static ADJECTIVES: &[&str] = &[ + "pretty", + "large", + "big", + "small", + "tall", + "short", + "long", + "handsome", + "plain", + "quaint", + "clean", + "elegant", + "easy", + "angry", + "crazy", + "helpful", + "mushy", + "odd", + "unsightly", + "adorable", + "important", + "inexpensive", + "cheap", + "expensive", + "fancy", +]; + +static COLOURS: &[&str] = &[ + "red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", + "orange", +]; + +static NOUNS: &[&str] = &[ + "table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", + "pizza", "mouse", "keyboard", +]; diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 4c4894161..cc46650c5 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -945,7 +945,8 @@ impl<'bump> DiffMachine<'bump> { for child in RealChildIterator::new(next_new, self.vdom) { let el = child.direct_id(); self.mutations.push_root(el); - self.mutations.insert_before(1); + todo!(); + // self.mutations.insert_before(1); } } else { self.stack.create_node( @@ -990,7 +991,8 @@ impl<'bump> DiffMachine<'bump> { for child in RealChildIterator::new(last_node, self.vdom) { let el = child.direct_id(); self.mutations.push_root(el); - self.mutations.insert_after(1); + // self.mutations.insert_after(1); + todo!(); } } else { eprintln!("key is not contained {:?}", key);