Refactor benchmarks (#472)
* Refactor benchmarks:
- Abstract tree creation behind a trait
- Implement trait for Yoga and Taffy
* Remove unused random_style module
* Set super deep benchmarks back to branching factor of 1
* Feature flag small and large benchmarks
* Split benchmark definitions into functions
* Add deep(auto) benchmarks
* Improve super-deep hierarchy benchmarks
6031fa21b5800de29385e8426daa4f4929754601 · por Nico Burns · 2023-10-12T17:57:23Z (hace 3 años) · padre 9e62e959c · ver el árbol en este commit
Delta semántico · 72
benches/benches/flexbox.rs
benches/benches/tree_creation.rs
benches/src/random_style.rs
benches/src/taffy_helpers.rs
benches/src/yoga_helpers.rs
src/tree/taffy_tree/tree.rs
diff --git a/benches/Cargo.toml b/benches/Cargo.toml
index d270b1326..b83939ce5 100644
--- a/benches/Cargo.toml
+++ b/benches/Cargo.toml
@@ -23,6 +23,9 @@ slotmap = { version = "1.0.6", optional = true }
[features]
yoga = ["dep:yoga", "dep:slotmap", "dep:ordered-float"]
+yoga-super-deep = ["yoga"]
+small = []
+large = []
[[bench]]
name = "tree_creation"
diff --git a/benches/benches/flexbox.rs b/benches/benches/flexbox.rs
index 393bb996e..a2c532149 100644
--- a/benches/benches/flexbox.rs
+++ b/benches/benches/flexbox.rs
@@ -1,155 +1,90 @@
//! This file includes benchmarks for very large, pseudo-randomly generated trees
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
-use rand::prelude::*;
-use rand_chacha::ChaCha8Rng;
+use rand::Rng;
use taffy::prelude::*;
-use taffy::style::Style;
-use taffy_benchmarks::{build_deep_tree, Randomizeable};
+use taffy::style::Dimension;
+use taffy::style::Style as TaffyStyle;
+
+use taffy_benchmarks::{BuildTreeExt, FixedStyleGenerator, GenStyle, TaffyTreeBuilder};
-#[cfg(feature = "yoga")]
-use slotmap::SlotMap;
#[cfg(feature = "yoga")]
use taffy_benchmarks::yoga_helpers;
#[cfg(feature = "yoga")]
-use yoga_helpers::yg;
-
-/// Build a random leaf node
-fn build_random_leaf(taffy: &mut Taffy, rng: &mut ChaCha8Rng) -> NodeId {
- taffy.new_with_children(Style::random(rng), &[]).unwrap()
-}
+use yoga_helpers::{yg, YogaTreeBuilder};
-/// A tree with many children that have shallow depth
-fn build_taffy_flat_hierarchy(total_node_count: u32) -> (Taffy, NodeId) {
- let mut taffy = Taffy::new();
- let mut rng = ChaCha8Rng::seed_from_u64(12345);
- let mut children = Vec::new();
- let mut node_count = 0;
-
- while node_count < total_node_count {
- let sub_children_count = rng.gen_range(1..=4);
- let sub_children: Vec<NodeId> =
- (0..sub_children_count).map(|_| build_random_leaf(&mut taffy, &mut rng)).collect();
- let node = taffy.new_with_children(Style::random(&mut rng), &sub_children).unwrap();
-
- children.push(node);
- node_count += 1 + sub_children_count;
+fn random_dimension(rng: &mut impl Rng) -> Dimension {
+ match rng.gen_range(0.0..=1.0) {
+ rand if rand < 0.2 => Dimension::Auto,
+ rand if rand < 0.8 => Dimension::Length(rng.gen_range(0.0..500.0)),
+ _ => Dimension::Percent(rng.gen_range(0.0..1.0)),
}
-
- let root = taffy.new_with_children(Style::DEFAULT, children.as_slice()).unwrap();
- (taffy, root)
}
-
-#[cfg(feature = "yoga")]
-/// A tree with many children that have shallow depth
-fn build_yoga_flat_hierarchy(total_node_count: u32) -> (yg::YogaTree, yg::NodeId) {
- let mut tree = SlotMap::new();
- let mut rng = ChaCha8Rng::seed_from_u64(12345);
- let mut children = Vec::new();
- let mut node_count = 0;
-
- while node_count < total_node_count {
- let sub_children_count = rng.gen_range(1..=4);
- let sub_children: Vec<yg::NodeId> = (0..sub_children_count)
- .map(|_| yoga_helpers::new_with_children(&mut tree, &Style::random(&mut rng), vec![]))
- .collect();
- let node = yoga_helpers::new_with_children(&mut tree, &Style::random(&mut rng), sub_children);
-
- children.push(node);
- node_count += 1 + sub_children_count;
+pub struct RandomStyleGenerator;
+impl GenStyle<TaffyStyle> for RandomStyleGenerator {
+ fn create_leaf_style(&mut self, rng: &mut impl Rng) -> TaffyStyle {
+ TaffyStyle { size: Size { width: random_dimension(rng), height: random_dimension(rng) }, ..Default::default() }
+ }
+ fn create_container_style(&mut self, rng: &mut impl Rng) -> TaffyStyle {
+ TaffyStyle { size: Size { width: random_dimension(rng), height: random_dimension(rng) }, ..Default::default() }
}
-
- let root = yoga_helpers::new_with_children(&mut tree, &Style::DEFAULT, children);
- (tree, root)
}
-/// A tree with a higher depth for a more realistic scenario
-fn build_taffy_deep_hierarchy(node_count: u32, branching_factor: u32) -> (Taffy, NodeId) {
- let mut rng = ChaCha8Rng::seed_from_u64(12345);
- let mut build_leaf_node = |taffy: &mut Taffy| build_random_leaf(taffy, &mut rng);
- let mut rng = ChaCha8Rng::seed_from_u64(12345);
- let mut build_flex_node =
- |taffy: &mut Taffy, children: Vec<NodeId>| taffy.new_with_children(Style::random(&mut rng), &children).unwrap();
-
- let mut taffy = Taffy::new();
- let tree = build_deep_tree(&mut taffy, node_count, branching_factor, &mut build_leaf_node, &mut build_flex_node);
- let root = taffy.new_with_children(Style::DEFAULT, &tree).unwrap();
- (taffy, root)
+/// A deep tree that matches the shape and styling that yoga use on their benchmarks
+fn build_flat_hierarchy<G: GenStyle<TaffyStyle>, TreeBuilder: BuildTreeExt<G>>(
+ target_node_count: u32,
+ style_generator: impl FnOnce() -> G,
+) -> (TreeBuilder::Tree, TreeBuilder::Node) {
+ let tree_builder = TreeBuilder::new(style_generator());
+ tree_builder.build_flat_hierarchy(target_node_count)
}
-#[cfg(feature = "yoga")]
-/// A tree with a higher depth for a more realistic scenario
-fn build_yoga_deep_hierarchy(node_count: u32, branching_factor: u32) -> (yg::YogaTree, yg::NodeId) {
- let mut rng = ChaCha8Rng::seed_from_u64(12345);
- let mut build_leaf_node =
- |tree: &mut yg::YogaTree| yoga_helpers::new_with_children(tree, &Style::random(&mut rng), vec![]);
- let mut rng = ChaCha8Rng::seed_from_u64(12345);
- let mut build_flex_node = |tree: &mut yg::YogaTree, children: Vec<yg::NodeId>| {
- yoga_helpers::new_with_children(tree, &Style::random(&mut rng), children)
- };
-
- let mut tree = SlotMap::new();
- let children = build_deep_tree(&mut tree, node_count, branching_factor, &mut build_leaf_node, &mut build_flex_node);
- let root = yoga_helpers::new_with_children(&mut tree, &Style::DEFAULT, children);
-
- (tree, root)
+/// A deep tree that matches the shape and styling that yoga use on their benchmarks
+fn build_deep_hierarchy<G: GenStyle<TaffyStyle>, TreeBuilder: BuildTreeExt<G>>(
+ node_count: u32,
+ branching_factor: u32,
+ style_generator: impl FnOnce() -> G,
+) -> (TreeBuilder::Tree, TreeBuilder::Node) {
+ let tree_builder = TreeBuilder::new(style_generator());
+ tree_builder.build_deep_hierarchy(node_count, branching_factor)
}
/// A deep tree that matches the shape and styling that yoga use on their benchmarks
-fn build_taffy_huge_nested_hierarchy(node_count: u32, branching_factor: u32) -> (Taffy, NodeId) {
- let style = Style {
- size: Size { width: Dimension::Length(10.0), height: Dimension::Length(10.0) },
- flex_grow: 1.0,
- ..Default::default()
- };
- let mut build_leaf_node = |taffy: &mut Taffy| taffy.new_leaf(style.clone()).unwrap();
- let mut build_flex_node =
- |taffy: &mut Taffy, children: Vec<NodeId>| taffy.new_with_children(style.clone(), &children).unwrap();
-
- let mut taffy = Taffy::new();
- let tree = build_deep_tree(&mut taffy, node_count, branching_factor, &mut build_leaf_node, &mut build_flex_node);
- let root = taffy.new_with_children(Style::DEFAULT, &tree).unwrap();
- (taffy, root)
+fn build_super_deep_hierarchy<G: GenStyle<TaffyStyle>, TreeBuilder: BuildTreeExt<G>>(
+ depth: u32,
+ nodes_per_level: u32,
+ style_generator: impl FnOnce() -> G,
+) -> (TreeBuilder::Tree, TreeBuilder::Node) {
+ let tree_builder = TreeBuilder::new(style_generator());
+ tree_builder.build_super_deep_hierarchy(depth, nodes_per_level)
}
-#[cfg(feature = "yoga")]
/// A deep tree that matches the shape and styling that yoga use on their benchmarks
-fn build_yoga_huge_nested_hierarchy(node_count: u32, branching_factor: u32) -> (yg::YogaTree, yg::NodeId) {
- let style = Style {
- size: Size { width: Dimension::Length(10.0), height: Dimension::Length(10.0) },
- flex_grow: 1.0,
- ..Default::default()
- };
- let mut build_leaf_node = |tree: &mut yg::YogaTree| -> yg::NodeId {
- let mut node = yg::Node::new();
- yoga_helpers::apply_taffy_style(&mut node, &style.clone());
- tree.insert(node)
- };
- let mut build_flex_node = |tree: &mut yg::YogaTree, children: Vec<yg::NodeId>| -> yg::NodeId {
- let mut node = yg::Node::new();
- yoga_helpers::apply_taffy_style(&mut node, &style.clone());
- for (i, child) in children.into_iter().enumerate() {
- node.insert_child(&mut tree[child], i as u32);
- }
- tree.insert(node)
- };
-
- let mut tree = SlotMap::new();
- let children = build_deep_tree(&mut tree, node_count, branching_factor, &mut build_leaf_node, &mut build_flex_node);
- let mut root = yg::Node::new();
- for (i, child) in children.into_iter().enumerate() {
- root.insert_child(&mut tree[child], i as u32);
- }
- let root = tree.insert(root);
- (tree, root)
+fn build_huge_nested_hierarchy<G: GenStyle<TaffyStyle>, TreeBuilder: BuildTreeExt<G>>(
+ node_count: u32,
+ branching_factor: u32,
+ style_generator: impl FnOnce() -> G,
+) -> (TreeBuilder::Tree, TreeBuilder::Node) {
+ let tree_builder = TreeBuilder::new(style_generator());
+ tree_builder.build_deep_hierarchy(node_count, branching_factor)
}
-fn taffy_benchmarks(c: &mut Criterion) {
+fn huge_nested_benchmarks(c: &mut Criterion) {
let mut group = c.benchmark_group("yoga 'huge nested'");
- for node_count in [1_000u32, 10_000, 100_000].iter() {
+ let style = Style { size: length(10.0), flex_grow: 1.0, ..Default::default() };
+ let style_gen = || FixedStyleGenerator(style.clone());
+ for node_count in [
+ #[cfg(feature = "small")]
+ 1_000u32,
+ 10_000,
+ #[cfg(feature = "large")]
+ 100_000,
+ ]
+ .iter()
+ {
#[cfg(feature = "yoga")]
group.bench_with_input(BenchmarkId::new("Yoga", node_count), node_count, |b, &node_count| {
b.iter_batched(
- || build_yoga_huge_nested_hierarchy(node_count, 10),
+ || build_huge_nested_hierarchy::<_, YogaTreeBuilder<_, _>>(node_count, 10, style_gen),
|(mut tree, root)| {
tree[root].calculate_layout(f32::INFINITY, f32::INFINITY, yg::Direction::LTR);
},
@@ -158,24 +93,34 @@ fn taffy_benchmarks(c: &mut Criterion) {
});
group.bench_with_input(BenchmarkId::new("Taffy", node_count), node_count, |b, &node_count| {
b.iter_batched(
- || build_taffy_huge_nested_hierarchy(node_count, 10),
+ || build_huge_nested_hierarchy::<_, TaffyTreeBuilder<_, _>>(node_count, 10, style_gen),
|(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
criterion::BatchSize::SmallInput,
)
});
}
group.finish();
+}
+fn wide_benchmarks(c: &mut Criterion) {
// Decrease sample size, because the tasks take longer
- let mut group = c.benchmark_group("big trees (wide)");
+ let mut group = c.benchmark_group("Wide tree");
group.sample_size(10);
- for node_count in [1_000u32, 10_000, 100_000].iter() {
+ for node_count in [
+ #[cfg(feature = "small")]
+ 1_000u32,
+ 10_000,
+ #[cfg(feature = "large")]
+ 100_000,
+ ]
+ .iter()
+ {
#[cfg(feature = "yoga")]
let benchmark_id = BenchmarkId::new(format!("Yoga (2-level hierarchy)"), node_count);
#[cfg(feature = "yoga")]
group.bench_with_input(benchmark_id, node_count, |b, &node_count| {
b.iter_batched(
- || build_yoga_flat_hierarchy(node_count),
+ || build_flat_hierarchy::<_, YogaTreeBuilder<_, _>>(node_count, || RandomStyleGenerator),
|(mut tree, root)| {
tree[root].calculate_layout(f32::INFINITY, f32::INFINITY, yg::Direction::LTR);
},
@@ -185,23 +130,30 @@ fn taffy_benchmarks(c: &mut Criterion) {
let benchmark_id = BenchmarkId::new(format!("Taffy (2-level hierarchy)"), node_count);
group.bench_with_input(benchmark_id, node_count, |b, &node_count| {
b.iter_batched(
- || build_taffy_flat_hierarchy(node_count),
+ || build_flat_hierarchy::<_, TaffyTreeBuilder<_, _>>(node_count, || RandomStyleGenerator),
|(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
criterion::BatchSize::SmallInput,
)
});
}
group.finish();
+}
+fn deep_random_benchmarks(c: &mut Criterion) {
// Decrease sample size, because the tasks take longer
- let mut group = c.benchmark_group("big trees (deep)");
+ let mut group = c.benchmark_group("Deep tree (random size)");
group.sample_size(10);
- let benches = [(4000, "(12-level hierarchy)"), (10_000, "(14-level hierarchy)"), (100_000, "(17-level hierarchy)")];
+ let benches = [
+ (4000, "(12-level hierarchy)"),
+ (10_000, "(14-level hierarchy)"),
+ #[cfg(feature = "large")]
+ (100_000, "(17-level hierarchy)"),
+ ];
for (node_count, label) in benches.iter() {
#[cfg(feature = "yoga")]
group.bench_with_input(BenchmarkId::new(format!("Yoga {label}"), node_count), node_count, |b, &node_count| {
b.iter_batched(
- || build_yoga_deep_hierarchy(node_count, 2),
+ || build_deep_hierarchy::<_, YogaTreeBuilder<_, _>>(node_count, 2, || RandomStyleGenerator),
|(mut tree, root)| {
tree[root].calculate_layout(f32::INFINITY, f32::INFINITY, yg::Direction::LTR);
},
@@ -210,30 +162,41 @@ fn taffy_benchmarks(c: &mut Criterion) {
});
group.bench_with_input(BenchmarkId::new(format!("Taffy {label}"), node_count), node_count, |b, &node_count| {
b.iter_batched(
- || build_taffy_deep_hierarchy(node_count, 2),
+ || build_deep_hierarchy::<_, TaffyTreeBuilder<_, _>>(node_count, 2, || RandomStyleGenerator),
|(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
criterion::BatchSize::SmallInput,
)
});
}
group.finish();
+}
- let mut group = c.benchmark_group("super deep (1000-level hierarchy)");
+fn deep_auto_benchmarks(c: &mut Criterion) {
+ // Decrease sample size, because the tasks take longer
+ let mut group = c.benchmark_group("Deep tree (auto size)");
group.sample_size(10);
- for node_count in [1000u32].iter() {
+ let style = Style { flex_grow: 1.0, margin: length(10.0), ..Default::default() };
+ let style_gen = || FixedStyleGenerator(style.clone());
+ let benches = [
+ (4000, "(12-level hierarchy)"),
+ (10_000, "(14-level hierarchy)"),
+ #[cfg(feature = "large")]
+ (100_000, "(17-level hierarchy)"),
+ ];
+ for (node_count, label) in benches.iter() {
#[cfg(feature = "yoga")]
- group.bench_with_input(BenchmarkId::new("Yoga", node_count), node_count, |b, &node_count| {
+ group.bench_with_input(BenchmarkId::new(format!("Yoga {label}"), node_count), node_count, |b, &node_count| {
b.iter_batched(
- || build_yoga_deep_hierarchy(node_count, 2),
+ || build_deep_hierarchy::<_, YogaTreeBuilder<_, _>>(node_count, 2, style_gen),
|(mut tree, root)| {
tree[root].calculate_layout(f32::INFINITY, f32::INFINITY, yg::Direction::LTR);
},
criterion::BatchSize::SmallInput,
)
});
- group.bench_with_input(BenchmarkId::new("Taffy", node_count), node_count, |b, &node_count| {
+ group.bench_with_input(BenchmarkId::new(format!("Taffy {label}"), node_count), node_count, |b, &node_count| {
b.iter_batched(
- || build_taffy_deep_hierarchy(node_count, 2),
+ || build_deep_hierarchy::<_, TaffyTreeBuilder<_, _>>(node_count, 2, style_gen),
|(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
criterion::BatchSize::SmallInput,
)
@@ -242,5 +205,65 @@ fn taffy_benchmarks(c: &mut Criterion) {
group.finish();
}
+fn super_deep_benchmarks(c: &mut Criterion) {
+ let mut group = c.benchmark_group("super deep");
+ group.sample_size(10);
+ struct SuperDeepStyleGen;
+ impl GenStyle<TaffyStyle> for SuperDeepStyleGen {
+ fn create_leaf_style(&mut self, _rng: &mut impl Rng) -> TaffyStyle {
+ // let flex_direction = if rng.gen::<f32>() < 0.5 { FlexDirection::Column } else { FlexDirection::Row };
+ let flex_direction = FlexDirection::Row;
+ Style { flex_direction, flex_grow: 1.0, margin: length(10.0), ..Default::default() }
+ }
+ fn create_container_style(&mut self, rng: &mut impl Rng) -> TaffyStyle {
+ self.create_leaf_style(rng)
+ }
+ }
+ for depth in [
+ #[cfg(feature = "small")]
+ 50u32,
+ 100,
+ #[cfg(feature = "large")]
+ 200,
+ ]
+ .iter()
+ {
+ // Yoga is particularly slow at these benchmarks, so we gate them behind a separate feature flag
+ #[cfg(all(feature = "yoga", feature = "yoga-super-deep"))]
+ group.bench_with_input(BenchmarkId::new("Yoga", depth), depth, |b, &depth| {
+ b.iter_batched(
+ || build_super_deep_hierarchy::<_, YogaTreeBuilder<_, _>>(depth, 3, || SuperDeepStyleGen),
+ |(mut tree, root)| {
+ tree[root].calculate_layout(800., 800., yg::Direction::LTR);
+ },
+ criterion::BatchSize::SmallInput,
+ )
+ });
+ group.bench_with_input(BenchmarkId::new("Taffy", depth), depth, |b, &depth| {
+ b.iter_batched(
+ || build_super_deep_hierarchy::<_, TaffyTreeBuilder<_, _>>(depth, 3, || SuperDeepStyleGen),
+ |(mut taffy, root)| {
+ taffy
+ .compute_layout(
+ root,
+ Size { width: AvailableSpace::Definite(800.), height: AvailableSpace::Definite(800.) },
+ )
+ .unwrap()
+ },
+ criterion::BatchSize::SmallInput,
+ )
+ });
+ }
+ group.finish();
+}
+
+fn taffy_benchmarks(c: &mut Criterion) {
+ huge_nested_benchmarks(c);
+ wide_benchmarks(c);
+ deep_auto_benchmarks(c);
+ deep_random_benchmarks(c);
+ super_deep_benchmarks(c);
+}
+
criterion_group!(benches, taffy_benchmarks);
criterion_main!(benches);
diff --git a/benches/benches/tree_creation.rs b/benches/benches/tree_creation.rs
index 0c2178d50..6f0c83dd1 100644
--- a/benches/benches/tree_creation.rs
+++ b/benches/benches/tree_creation.rs
@@ -48,14 +48,14 @@ fn build_yoga_flat_hierarchy(total_node_count: u32) -> (yg::YogaTree, yg::NodeId
while node_count < total_node_count {
let sub_children_count = rng.gen_range(1..=4);
let sub_children: Vec<yg::NodeId> =
- (0..sub_children_count).map(|_| yoga_helpers::new_default_style_with_children(&mut tree, vec![])).collect();
- let node = yoga_helpers::new_default_style_with_children(&mut tree, sub_children);
+ (0..sub_children_count).map(|_| yoga_helpers::new_default_style_with_children(&mut tree, &[])).collect();
+ let node = yoga_helpers::new_default_style_with_children(&mut tree, &sub_children);
children.push(node);
node_count += 1 + sub_children_count;
}
- let root = yoga_helpers::new_default_style_with_children(&mut tree, children);
+ let root = yoga_helpers::new_default_style_with_children(&mut tree, &children);
(tree, root)
}
diff --git a/benches/src/lib.rs b/benches/src/lib.rs
index 42c5a4662..6d67c6774 100644
--- a/benches/src/lib.rs
+++ b/benches/src/lib.rs
@@ -1,31 +1,127 @@
+// As each benchmark suite is compiled as a seperate crate and uses different helpers, we end up with a bunch
+// of false positives for this lint. So let's just disable it for this code.
+#![allow(dead_code)]
+
+pub mod taffy_helpers;
+pub use taffy_helpers::TaffyTreeBuilder;
+
#[cfg(feature = "yoga")]
pub mod yoga_helpers;
+#[cfg(feature = "yoga")]
+pub use yoga_helpers::YogaTreeBuilder;
+
+use rand::distributions::uniform::SampleRange;
+use rand::{Rng, SeedableRng};
+use rand_chacha::ChaCha8Rng;
+use taffy::style::Style as TaffyStyle;
+
+pub const STANDARD_RNG_SEED: u64 = 12345;
+
+pub trait GenStyle<Style: Default> {
+ fn create_leaf_style(&mut self, rng: &mut impl Rng) -> Style;
+ fn create_container_style(&mut self, rng: &mut impl Rng) -> Style;
+ fn create_root_style(&mut self, _rng: &mut impl Rng) -> Style {
+ Default::default()
+ }
+}
+
+pub struct FixedStyleGenerator(pub TaffyStyle);
+impl GenStyle<TaffyStyle> for FixedStyleGenerator {
+ fn create_leaf_style(&mut self, _rng: &mut impl Rng) -> TaffyStyle {
+ self.0.clone()
+ }
+ fn create_container_style(&mut self, _rng: &mut impl Rng) -> TaffyStyle {
+ self.0.clone()
+ }
+}
+
+pub trait BuildTree<R: Rng, G: GenStyle<TaffyStyle>>: Sized {
+ type Tree;
+ type Node: Clone;
+
+ fn with_rng(rng: R, style_generator: G) -> Self;
+
+ fn random_usize(&mut self, range: impl SampleRange<usize>) -> usize;
+ fn create_leaf_node(&mut self) -> Self::Node;
+ fn create_container_node(&mut self, children: &[Self::Node]) -> Self::Node;
+ fn set_root_children(&mut self, children: &[Self::Node]);
+ fn total_node_count(&mut self) -> usize;
+ fn into_tree_and_root(self) -> (Self::Tree, Self::Node);
+
+ fn build_n_leaf_nodes(&mut self, n: usize) -> Vec<Self::Node> {
+ (0..n).map(|_| self.create_leaf_node()).collect()
+ }
-mod random_style;
-pub use random_style::Randomizeable;
-
-/// A helper function to recursively construct a deep tree
-#[allow(dead_code)]
-pub fn build_deep_tree<T, N>(
- tree: &mut T,
- max_nodes: u32,
- branching_factor: u32,
- create_leaf_node: &mut impl FnMut(&mut T) -> N,
- create_container_node: &mut impl FnMut(&mut T, Vec<N>) -> N,
-) -> Vec<N> {
- if max_nodes <= branching_factor {
- // Build leaf nodes
- return (0..max_nodes).map(|_| create_leaf_node(tree)).collect();
- }
-
- // Add another layer to the tree
- // Each child gets an equal amount of the remaining nodes
- (0..branching_factor)
- .map(|_| {
- let max_nodes = (max_nodes - branching_factor) / branching_factor;
- let sub_children =
- build_deep_tree(tree, max_nodes, branching_factor, create_leaf_node, create_container_node);
- create_container_node(tree, sub_children)
- })
- .collect()
+ /// A helper function to recursively construct a deep tree
+ fn build_deep_tree(&mut self, max_nodes: u32, branching_factor: u32) -> Vec<Self::Node> {
+ if max_nodes <= branching_factor {
+ // Build leaf nodes
+ return (0..max_nodes).map(|_| self.create_leaf_node()).collect();
+ }
+
+ // Add another layer to the tree
+ // Each child gets an equal amount of the remaining nodes
+ (0..branching_factor)
+ .map(|_| {
+ let max_nodes = (max_nodes - branching_factor) / branching_factor;
+ let children = self.build_deep_tree(max_nodes, branching_factor);
+ self.create_container_node(&children)
+ })
+ .collect()
+ }
+
+ /// A helper function to recursively construct a deep tree
+ fn build_super_deep_hierarchy(mut self, depth: u32, nodes_per_level: u32) -> (Self::Tree, Self::Node) {
+ let mut children = Vec::with_capacity(nodes_per_level as usize);
+ for _ in 0..depth {
+ let node_with_children = self.create_container_node(&children);
+
+ children.clear();
+ children.push(node_with_children);
+ for _ in 0..(nodes_per_level - 1) {
+ children.push(self.create_leaf_node())
+ }
+ }
+ self.set_root_children(&children);
+ self.into_tree_and_root()
+ }
+
+ /// A tree with a higher depth for a more realistic scenario
+ fn build_deep_hierarchy(mut self, node_count: u32, branching_factor: u32) -> (Self::Tree, Self::Node) {
+ let children = self.build_deep_tree(node_count, branching_factor);
+ self.set_root_children(&children);
+ self.into_tree_and_root()
+ }
+
+ /// A tree with many children that have shallow depth
+ fn build_flat_hierarchy(mut self, target_node_count: u32) -> (Self::Tree, Self::Node) {
+ let mut children = Vec::new();
+
+ while self.total_node_count() < target_node_count as usize {
+ let count = self.random_usize(1..=4);
+ let sub_children = self.build_n_leaf_nodes(count);
+ let node = self.create_container_node(&sub_children);
+ children.push(node);
+ }
+
+ self.set_root_children(&children);
+ self.into_tree_and_root()
+ }
+}
+
+pub trait BuildTreeExt<G: GenStyle<TaffyStyle>>: BuildTree<ChaCha8Rng, G> {
+ fn with_seed(seed: u64, style_generator: G) -> Self
+ where
+ Self: Sized,
+ {
+ let rng = ChaCha8Rng::seed_from_u64(seed);
+ Self::with_rng(rng, style_generator)
+ }
+
+ fn new(style_generator: G) -> Self
+ where
+ Self: Sized,
+ {
+ Self::with_seed(STANDARD_RNG_SEED, style_generator)
+ }
}
diff --git a/benches/src/random_style.rs b/benches/src/random_style.rs
deleted file mode 100644
index f7ef6154c..000000000
--- a/benches/src/random_style.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-//! Pseudo-random generation of data (e.g. UI nodes) to use in benchmarks.
-
-use rand::Rng;
-
-use taffy::geometry::Size;
-use taffy::style::Dimension;
-use taffy::style::Style;
-
-/// A trait for generating pseudo-random instances.
-pub trait Randomizeable {
- /// Generate a pseudo-random instance of this type.
- ///
- /// This can be useful for benchmarking.
- fn random<R>(rng: &mut R) -> Self
- where
- R: Rng;
-}
-
-impl Randomizeable for Dimension {
- fn random<R>(rng: &mut R) -> Self
- where
- R: Rng,
- {
- let switch: f32 = rng.gen_range(0.0..=1.0);
-
- if switch < 0.2 {
- Dimension::Auto
- } else if switch < 0.8 {
- Dimension::Length(rng.gen_range(0.0..500.0))
- } else {
- Dimension::Percent(rng.gen_range(0.0..1.0))
- }
- }
-}
-
-impl Randomizeable for Size<Dimension> {
- fn random<R>(rng: &mut R) -> Self
- where
- R: Rng,
- {
- Size { width: Dimension::random(rng), height: Dimension::random(rng) }
- }
-}
-
-impl Randomizeable for Style {
- fn random<R>(rng: &mut R) -> Self
- where
- R: Rng,
- {
- // TODO: Add more attributes
- Style { size: Size::<Dimension>::random(rng), ..Default::default() }
- }
-}
diff --git a/benches/src/taffy_helpers.rs b/benches/src/taffy_helpers.rs
new file mode 100644
index 000000000..e0fbb6b2a
--- /dev/null
+++ b/benches/src/taffy_helpers.rs
@@ -0,0 +1,55 @@
+use rand::distributions::uniform::SampleRange;
+use rand::Rng;
+use rand_chacha::ChaCha8Rng;
+use taffy::style::Style as TaffyStyle;
+use taffy::tree::NodeId as TaffyNodeId;
+use taffy::Taffy as TaffyTree;
+
+use super::{BuildTree, BuildTreeExt, GenStyle};
+
+pub struct TaffyTreeBuilder<R: Rng, G: GenStyle<TaffyStyle>> {
+ rng: R,
+ style_generator: G,
+ tree: TaffyTree,
+ root: TaffyNodeId,
+}
+
+// Implement the BuildTree trait
+impl<R: Rng, G: GenStyle<TaffyStyle>> BuildTree<R, G> for TaffyTreeBuilder<R, G> {
+ type Tree = TaffyTree;
+ type Node = TaffyNodeId;
+
+ fn with_rng(mut rng: R, mut style_generator: G) -> Self {
+ let mut tree = TaffyTree::new();
+ let root = tree.new_leaf(style_generator.create_root_style(&mut rng)).unwrap();
+ TaffyTreeBuilder { rng, style_generator, tree, root }
+ }
+
+ fn random_usize(&mut self, range: impl SampleRange<usize>) -> usize {
+ self.rng.gen_range(range)
+ }
+
+ fn create_leaf_node(&mut self) -> Self::Node {
+ let style = self.style_generator.create_leaf_style(&mut self.rng);
+ self.tree.new_leaf(style).unwrap()
+ }
+
+ fn create_container_node(&mut self, children: &[Self::Node]) -> Self::Node {
+ let style = self.style_generator.create_container_style(&mut self.rng);
+ self.tree.new_with_children(style, children).unwrap()
+ }
+
+ fn total_node_count(&mut self) -> usize {
+ self.tree.total_node_count()
+ }
+
+ fn set_root_children(&mut self, children: &[Self::Node]) {
+ self.tree.set_children(self.root, children).unwrap();
+ }
+
+ fn into_tree_and_root(self) -> (Self::Tree, Self::Node) {
+ (self.tree, self.root)
+ }
+}
+
+impl<G: GenStyle<TaffyStyle>> BuildTreeExt<G> for TaffyTreeBuilder<ChaCha8Rng, G> {}
diff --git a/benches/src/yoga_helpers.rs b/benches/src/yoga_helpers.rs
index 3a5a96ba2..b05d7e61d 100644
--- a/benches/src/yoga_helpers.rs
+++ b/benches/src/yoga_helpers.rs
@@ -1,9 +1,15 @@
#![allow(dead_code)]
+
+use rand::distributions::uniform::SampleRange;
+use rand::Rng;
+use rand_chacha::ChaCha8Rng;
use slotmap::{DefaultKey, SlotMap};
+use super::{BuildTree, BuildTreeExt, GenStyle};
+
pub mod yg {
pub use ordered_float::OrderedFloat;
- use slotmap::{DefaultKey, SlotMap};
+ pub use slotmap::{DefaultKey, SlotMap};
pub use yoga::types::*;
pub use yoga::Node;
@@ -13,31 +19,105 @@ pub mod yg {
mod tf {
pub use taffy::prelude::*;
}
+use tf::Style as TaffyStyle;
+
+pub struct YogaTreeBuilder<R: Rng, G: GenStyle<TaffyStyle>> {
+ rng: R,
+ style_generator: G,
+ tree: yg::YogaTree,
+ root: yg::DefaultKey,
+}
+
+// Implement the BuildTree trait
+impl<R: Rng, G: GenStyle<TaffyStyle>> BuildTree<R, G> for YogaTreeBuilder<R, G> {
+ type Tree = yg::YogaTree;
+ type Node = DefaultKey;
+
+ fn with_rng(mut rng: R, mut style_generator: G) -> Self {
+ let mut tree = SlotMap::new();
+ let root = create_yg_node(&mut tree, &style_generator.create_root_style(&mut rng), &[]);
+ YogaTreeBuilder { rng, style_generator, tree, root }
+ }
+
+ fn random_usize(&mut self, range: impl SampleRange<usize>) -> usize {
+ self.rng.gen_range(range)
+ }
-pub fn new_default_style_with_children(
- tree: &mut SlotMap<DefaultKey, yg::Node>,
- children: Vec<DefaultKey>,
-) -> DefaultKey {
+ fn create_leaf_node(&mut self) -> Self::Node {
+ let style = self.style_generator.create_leaf_style(&mut self.rng);
+ create_yg_node(&mut self.tree, &style, &[])
+ }
+
+ fn create_container_node(&mut self, children: &[Self::Node]) -> Self::Node {
+ let style = self.style_generator.create_container_style(&mut self.rng);
+ create_yg_node(&mut self.tree, &style, &children)
+ }
+
+ fn set_root_children(&mut self, children: &[Self::Node]) {
+ set_node_children(&mut self.tree, self.root, &children);
+ }
+
+ fn total_node_count(&mut self) -> usize {
+ self.tree.len()
+ }
+
+ fn into_tree_and_root(self) -> (Self::Tree, Self::Node) {
+ (self.tree, self.root)
+ }
+}
+
+impl<G: GenStyle<TaffyStyle>> BuildTreeExt<G> for YogaTreeBuilder<ChaCha8Rng, G> {}
+
+// impl<R: Rng, G: GenStyle<TaffyStyle>> YogaTreeBuilder<R, G> {
+// /// Create a YogaTreeBuilder with a standard rng from a style generator
+// fn new<NG: GenStyle<TaffyStyle>>(mut style_generator: NG) -> YogaTreeBuilder<ChaCha8Rng, NG> {
+// let mut rng = ChaCha8Rng::seed_from_u64(STANDARD_RNG_SEED);
+// let mut tree = SlotMap::new();
+// let root = create_yg_node(&mut tree, &style_generator.create_root_style(&mut rng), &[]);
+// YogaTreeBuilder { rng, style_generator, tree, root }
+// }
+
+// /// Create a YogaTreeBuilder with a standard rng from a style generator
+// fn with_seed<NG: GenStyle<TaffyStyle>>(seed: u64, mut style_generator: NG) -> YogaTreeBuilder<ChaCha8Rng, NG> {
+// let mut rng = ChaCha8Rng::seed_from_u64(seed);
+// let mut tree = SlotMap::new();
+// let root = create_yg_node(&mut tree, &style_generator.create_root_style(&mut rng), &[]);
+// YogaTreeBuilder { rng, style_generator, tree, root }
+// }
+
+// /// Create a YogaTreeBuilder from a random number generator and a style generator
+// fn with_rng<NR: Rng, NG: GenStyle<TaffyStyle>>(mut rng: NR, mut style_generator: NG) -> YogaTreeBuilder<NR, NG> {
+// let mut tree = SlotMap::new();
+// let root = create_yg_node(&mut tree, &style_generator.create_root_style(&mut rng), &[]);
+// YogaTreeBuilder { rng, style_generator, tree, root }
+// }
+// }
+
+fn create_yg_node(tree: &mut yg::YogaTree, style: &tf::Style, children: &[yg::DefaultKey]) -> yg::DefaultKey {
let mut node = yg::Node::new();
+ apply_taffy_style(&mut node, &style);
for (i, child) in children.into_iter().enumerate() {
- node.insert_child(&mut tree[child], i as u32);
+ node.insert_child(&mut tree[*child], i as u32);
}
tree.insert(node)
}
-pub fn new_with_children(
- tree: &mut SlotMap<DefaultKey, yg::Node>,
- style: &tf::Style,
- children: Vec<DefaultKey>,
-) -> DefaultKey {
+pub fn new_default_style_with_children(tree: &mut yg::YogaTree, children: &[yg::DefaultKey]) -> yg::DefaultKey {
let mut node = yg::Node::new();
- apply_taffy_style(&mut node, style);
for (i, child) in children.into_iter().enumerate() {
- node.insert_child(&mut tree[child], i as u32);
+ node.insert_child(&mut tree[*child], i as u32);
}
tree.insert(node)
}
+fn set_node_children(tree: &mut yg::YogaTree, node_id: yg::DefaultKey, children: &[yg::DefaultKey]) {
+ // TODO: clear existing children.
+ for (i, child_id) in children.into_iter().enumerate() {
+ let [node, child] = tree.get_disjoint_mut([node_id, *child_id]).unwrap();
+ node.insert_child(child, i as u32);
+ }
+}
+
fn into_yg_units(dim: impl Into<tf::Dimension>) -> yg::StyleUnit {
match dim.into() {
tf::Dimension::Auto => yg::StyleUnit::Auto,
@@ -93,7 +173,7 @@ fn content_into_justify(align: Option<tf::JustifyContent>) -> yg::Justify {
}
}
-pub fn apply_taffy_style(node: &mut yg::Node, style: &tf::Style) {
+fn apply_taffy_style(node: &mut yg::Node, style: &tf::Style) {
// display
node.set_display(match style.display {
tf::Display::None => yg::Display::None,
diff --git a/src/tree/taffy_tree/tree.rs b/src/tree/taffy_tree/tree.rs
index effd39000..6ead32954 100644
--- a/src/tree/taffy_tree/tree.rs
+++ b/src/tree/taffy_tree/tree.rs
@@ -400,6 +400,11 @@ impl<NodeContext> Taffy<NodeContext> {
Ok(self.children[parent_key][child_index])
}
+ /// Returns the total number of nodes in the tree
+ pub fn total_node_count(&self) -> usize {
+ self.nodes.len()
+ }
+
/// Returns the number of children of the `parent` node
pub fn child_count(&self, parent: NodeId) -> TaffyResult<usize> {
Ok(self.children[parent.into()].len())