Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSS Grid Benchmarks #340

Merged
merged 15 commits into from
Jan 27, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@ serde = ["dep:serde"]
random = ["dep:rand"]
yoga_benchmark = []
debug = []
profile = []

[dev-dependencies]
criterion = "0.4"
@@ -57,8 +58,13 @@ path = "benches/complex.rs"
harness = false

[[bench]]
name = "big_tree"
path = "benches/big_tree.rs"
name = "big_tree_flexbox"
path = "benches/big_tree_flexbox.rs"
harness = false

[[bench]]
name = "big_tree_grid"
path = "benches/big_tree_grid.rs"
harness = false

[workspace]
29 changes: 4 additions & 25 deletions benches/big_tree.rs → benches/big_tree_flexbox.rs
Original file line number Diff line number Diff line change
@@ -6,8 +6,11 @@ use taffy::prelude::*;
use taffy::randomizable::Randomizeable;
use taffy::style::Style;

mod helpers;
use helpers::build_deep_tree;

#[cfg(feature = "yoga_benchmark")]
mod yoga_helpers;
use helpers::yoga_helpers;
#[cfg(feature = "yoga_benchmark")]
use slotmap::SlotMap;
#[cfg(feature = "yoga_benchmark")]
@@ -62,30 +65,6 @@ fn build_yoga_flat_hierarchy(total_node_count: u32) -> (yg::YogaTree, Node) {
(tree, root)
}

/// A helper function to recursively construct a deep tree
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_flex_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_flex_node);
create_flex_node(tree, sub_children)
})
.collect()
}

/// A tree with a higher depth for a more realistic scenario
fn build_taffy_deep_hierarchy(node_count: u32, branching_factor: u32) -> (Taffy, Node) {
let mut rng = ChaCha8Rng::seed_from_u64(12345);
161 changes: 161 additions & 0 deletions benches/big_tree_grid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
//! 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 std::iter;
use taffy::prelude::*;
use taffy::style::Style;

/// Build a random leaf node
fn build_random_leaf(taffy: &mut Taffy, _rng: &mut ChaCha8Rng) -> Node {
taffy.new_with_children(Style { size: points(20.0), ..Default::default() }, &[]).unwrap()
}

fn random_grid_track<R: Rng>(rng: &mut R) -> TrackSizingFunction {
let switch: f32 = rng.gen_range(0.0..=1.0);
if switch < 0.1 {
auto()
} else if switch < 0.2 {
min_content()
} else if switch < 0.3 {
max_content()
} else if switch < 0.5 {
flex(1.0)
} else if switch < 0.6 {
minmax(points(0.0), flex(1.0))
} else if switch < 0.8 {
points(40.0)
} else {
percent(0.3)
}
}

fn random_nxn_grid_style<R: Rng>(rng: &mut R, track_count: usize) -> Style {
Style {
display: Display::Grid,
grid_template_columns: iter::from_fn(|| Some(random_grid_track(rng))).take(track_count).collect(),
grid_template_rows: iter::from_fn(|| Some(random_grid_track(rng))).take(track_count).collect(),
..Default::default()
}
}

/// A tree with many children that have shallow depth
fn build_grid_flat_hierarchy(col_count: usize, row_count: usize) -> (Taffy, Node) {
let mut taffy = Taffy::new();
let mut rng = ChaCha8Rng::seed_from_u64(12345);

let style = Style {
display: Display::Grid,
grid_template_columns: iter::from_fn(|| Some(random_grid_track(&mut rng))).take(col_count).collect(),
grid_template_rows: iter::from_fn(|| Some(random_grid_track(&mut rng))).take(row_count).collect(),
..Default::default()
};

let children: Vec<_> =
iter::from_fn(|| Some(build_random_leaf(&mut taffy, &mut rng))).take(col_count * row_count).collect();

let root = taffy.new_with_children(style, children.as_slice()).unwrap();
(taffy, root)
}

/// A helper function to recursively construct a deep tree
pub fn build_deep_grid_tree(
tree: &mut Taffy,
levels: usize,
track_count: usize,
create_leaf_node: &mut impl FnMut(&mut Taffy) -> Node,
create_container_node: &mut impl FnMut(&mut Taffy, Vec<Node>) -> Node,
) -> Vec<Node> {
// The extra one is for a position:absolute child
let child_count = track_count.pow(2) + 1;

if levels == 1 {
// Build leaf nodes
return (0..child_count).map(|_| create_leaf_node(tree)).collect();
}

// Add another layer to the tree
// Each child gets an equal amount of the remaining nodes
(0..child_count)
.map(|_| {
let sub_children =
build_deep_grid_tree(tree, levels - 1, track_count, create_leaf_node, create_container_node);
tree.set_style(sub_children[child_count - 1], {
let mut style = tree.style(sub_children[child_count - 1]).unwrap().clone();
style.position = Position::Absolute;
style.size = percent(0.5);
style
})
.unwrap();
create_container_node(tree, sub_children)
})
.collect()
}

/// A tree with a higher depth for a more realistic scenario
fn build_taffy_deep_grid_hierarchy(levels: usize, track_count: usize) -> (Taffy, Node) {
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<Node>| {
taffy.new_with_children(random_nxn_grid_style(&mut rng, track_count), &children).unwrap()
};

let mut taffy = Taffy::new();
let tree = build_deep_grid_tree(&mut taffy, levels, track_count, &mut build_leaf_node, &mut build_flex_node);
let root = taffy.new_with_children(Style::DEFAULT, &tree).unwrap();
(taffy, root)
}

fn taffy_benchmarks(c: &mut Criterion) {
let mut group = c.benchmark_group("grid/wide");
group.sample_size(10);
for track_count in [31usize, 100, 316].iter() {
group.bench_with_input(
BenchmarkId::new(format!("{c}x{c}", c = track_count), track_count.pow(2)),
track_count,
|b, &track_count| {
b.iter_batched(
|| build_grid_flat_hierarchy(track_count, track_count),
|(mut taffy, root)| taffy.compute_layout(root, points(12000.0)).unwrap(),
criterion::BatchSize::SmallInput,
)
},
);
}
group.finish();

let mut group = c.benchmark_group("grid/deep");
group.sample_size(10);
for (tracks, levels) in [(3, 3), (3, 4) /*, (3, 5)*/, (2, 3), (2, 4), (2, 5), (2, 6)].iter() {
let children_per_level: usize = (tracks * tracks) + 1;
group.bench_with_input(
BenchmarkId::new(format!("{c}x{c}", c = tracks), children_per_level.pow(*levels as u32)),
&(*levels, *tracks),
|b, &(levels, tracks)| {
b.iter_batched(
|| build_taffy_deep_grid_hierarchy(levels, tracks),
|(mut taffy, root)| taffy.compute_layout(root, points(12000.0)).unwrap(),
criterion::BatchSize::SmallInput,
)
},
);
}
group.finish();

let mut group = c.benchmark_group("grid/superdeep");
group.sample_size(10);
for levels in [10, 20].iter() {
group.bench_with_input(BenchmarkId::new("1x1", levels), levels, |b, &levels| {
b.iter_batched(
|| build_taffy_deep_grid_hierarchy(levels, 1),
|(mut taffy, root)| taffy.compute_layout(root, max_content()).unwrap(),
criterion::BatchSize::SmallInput,
)
});
}
group.finish();
}

criterion_group!(benches, taffy_benchmarks);
criterion_main!(benches);
27 changes: 27 additions & 0 deletions benches/helpers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#[cfg(feature = "yoga_benchmark")]
mod yoga_helpers;

/// A helper function to recursively construct a deep tree
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()
}
File renamed without changes.
8 changes: 4 additions & 4 deletions src/compute/mod.rs
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ use self::flexbox::FlexboxAlgorithm;
use self::grid::CssGridAlgorithm;
use self::leaf::LeafAlgorithm;

#[cfg(feature = "debug")]
#[cfg(any(feature = "debug", feature = "profile"))]
use crate::debug::NODE_LOGGER;

/// Updates the stored layout of the provided `node` and its children
@@ -130,7 +130,7 @@ fn compute_node_layout(
run_mode: RunMode,
sizing_mode: SizingMode,
) -> SizeAndBaselines {
#[cfg(feature = "debug")]
#[cfg(any(feature = "debug", feature = "profile"))]
NODE_LOGGER.push_node(node);
#[cfg(feature = "debug")]
println!();
@@ -144,7 +144,7 @@ fn compute_node_layout(
NODE_LOGGER.labelled_debug_log("CACHE", cached_size_and_baselines.size);
#[cfg(feature = "debug")]
debug_log_node(known_dimensions, parent_size, available_space, run_mode, sizing_mode);
#[cfg(feature = "debug")]
#[cfg(any(feature = "debug", feature = "profile"))]
NODE_LOGGER.pop_node();
return cached_size_and_baselines;
}
@@ -234,7 +234,7 @@ fn compute_node_layout(

#[cfg(feature = "debug")]
NODE_LOGGER.labelled_debug_log("RESULT", computed_size_and_baselines.size);
#[cfg(feature = "debug")]
#[cfg(any(feature = "debug", feature = "profile"))]
NODE_LOGGER.pop_node();

computed_size_and_baselines
24 changes: 23 additions & 1 deletion src/debug.rs
Original file line number Diff line number Diff line change
@@ -104,5 +104,27 @@ impl DebugLogger {
}
}

#[cfg(feature = "debug")]
#[cfg(any(feature = "debug", feature = "profile"))]
pub(crate) static NODE_LOGGER: DebugLogger = DebugLogger::new();

#[cfg(feature = "profile")]
#[allow(unused_macros)]
macro_rules! time {
($label:expr, $($code:tt)*) => {
let start = ::std::time::Instant::now();
$($code)*
let duration = ::std::time::Instant::now().duration_since(start);
crate::debug::NODE_LOGGER.log(format_args!("Performed {} in {}ms", $label, duration.as_millis()));
};
}

#[cfg(not(feature = "profile"))]
#[allow(unused_macros)]
macro_rules! time {
($label:expr, $($code:tt)*) => {
$($code)*
};
}

#[allow(unused_imports)]
pub(crate) use time;