Skip to content

Commit

Permalink
Refactor benchmarks (#472)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
nicoburns committed Oct 12, 2023
1 parent 9e62e95 commit 6031fa2
Show file tree
Hide file tree
Showing 8 changed files with 446 additions and 237 deletions.
3 changes: 3 additions & 0 deletions benches/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
303 changes: 163 additions & 140 deletions benches/benches/flexbox.rs

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions benches/benches/tree_creation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
150 changes: 123 additions & 27 deletions benches/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}
53 changes: 0 additions & 53 deletions benches/src/random_style.rs

This file was deleted.

55 changes: 55 additions & 0 deletions benches/src/taffy_helpers.rs
Original file line number Diff line number Diff line change
@@ -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> {}
Loading

0 comments on commit 6031fa2

Please sign in to comment.