diff --git a/Cargo.toml b/Cargo.toml index 9ed7c4871..918fcdd47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,11 @@ taffy = { path = ".", features = ["random"] } lto = true panic = 'abort' +[[bench]] +name = "tree_creation" +path = "benches/tree_creation.rs" +harness = false + [[bench]] name = "flexbox" path = "benches/flexbox.rs" diff --git a/benches/helpers/mod.rs b/benches/helpers/mod.rs index 66269b571..617fc6897 100644 --- a/benches/helpers/mod.rs +++ b/benches/helpers/mod.rs @@ -2,6 +2,7 @@ pub mod yoga_helpers; /// A helper function to recursively construct a deep tree +#[allow(dead_code)] pub fn build_deep_tree( tree: &mut T, max_nodes: u32, diff --git a/benches/helpers/yoga_helpers.rs b/benches/helpers/yoga_helpers.rs index 165ebdfea..787ea569e 100644 --- a/benches/helpers/yoga_helpers.rs +++ b/benches/helpers/yoga_helpers.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use slotmap::{DefaultKey, SlotMap}; pub mod yg { @@ -12,6 +13,17 @@ mod tf { pub use taffy::prelude::*; } +pub fn new_default_style_with_children( + tree: &mut SlotMap, + children: Vec, +) -> DefaultKey { + let mut node = yg::Node::new(); + for (i, child) in children.into_iter().enumerate() { + node.insert_child(&mut tree[child], i as u32); + } + tree.insert(node) +} + pub fn new_with_children( tree: &mut SlotMap, style: &tf::Style, diff --git a/benches/tree_creation.rs b/benches/tree_creation.rs new file mode 100644 index 000000000..10ce6a898 --- /dev/null +++ b/benches/tree_creation.rs @@ -0,0 +1,99 @@ +//! This file includes benchmarks for tree creation +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; +use rand::prelude::*; +use rand_chacha::ChaCha8Rng; +use taffy::prelude::*; +use taffy::style::Style; + +mod helpers; + +#[cfg(feature = "yoga_benchmark")] +use helpers::yoga_helpers; +#[cfg(feature = "yoga_benchmark")] +use slotmap::SlotMap; +#[cfg(feature = "yoga_benchmark")] +use yoga_helpers::yg; + +/// Build a random leaf node +fn build_random_leaf(taffy: &mut Taffy) -> Node { + taffy.new_with_children(Style::DEFAULT, &[]).unwrap() +} + +/// A tree with many children that have shallow depth +fn build_taffy_flat_hierarchy(total_node_count: u32, use_with_capacity: bool) -> (Taffy, Node) { + let mut taffy = if use_with_capacity { Taffy::with_capacity(total_node_count as usize) } else { 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 = (0..sub_children_count).map(|_| build_random_leaf(&mut taffy)).collect(); + let node = taffy.new_with_children(Style::DEFAULT, &sub_children).unwrap(); + + children.push(node); + node_count += 1 + sub_children_count; + } + + let root = taffy.new_with_children(Style::DEFAULT, children.as_slice()).unwrap(); + (taffy, root) +} + +#[cfg(feature = "yoga_benchmark")] +/// A tree with many children that have shallow depth +fn build_yoga_flat_hierarchy(total_node_count: u32) -> (yg::YogaTree, Node) { + 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 = + (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); + + children.push(node); + node_count += 1 + sub_children_count; + } + + let root = yoga_helpers::new_default_style_with_children(&mut tree, children); + (tree, root) +} + +fn taffy_benchmarks(c: &mut Criterion) { + let mut group = c.benchmark_group("Tree creation"); + for node_count in [1_000u32, 10_000, 100_000].iter() { + #[cfg(feature = "yoga_benchmark")] + let benchmark_id = BenchmarkId::new(format!("Yoga"), node_count); + #[cfg(feature = "yoga_benchmark")] + group.bench_with_input(benchmark_id, node_count, |b, &node_count| { + b.iter(|| { + let (taffy, root) = build_yoga_flat_hierarchy(node_count); + std::hint::black_box(taffy); + std::hint::black_box(root); + }) + }); + let benchmark_id = BenchmarkId::new(format!("Taffy::new"), node_count); + group.bench_with_input(benchmark_id, node_count, |b, &node_count| { + b.iter(|| { + let (tree, root) = build_taffy_flat_hierarchy(node_count, false); + std::hint::black_box(tree); + std::hint::black_box(root); + }) + }); + + let benchmark_id = BenchmarkId::new(format!("Taffy::with_capacity"), node_count); + group.bench_with_input(benchmark_id, node_count, |b, &node_count| { + b.iter(|| { + let (tree, root) = build_taffy_flat_hierarchy(node_count, true); + std::hint::black_box(tree); + std::hint::black_box(root); + }) + }); + } + group.finish(); +} + +criterion_group!(benches, taffy_benchmarks); +criterion_main!(benches);