Use `struct NodeId(u64)` instead of `slotmap::DefaultKey` in all public APIs (#426)
1//! This file includes benchmarks for very large, pseudo-randomly generated trees
2use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
3use rand::prelude::*;
4use rand_chacha::ChaCha8Rng;
5use std::iter;
6use taffy::prelude::*;
7use taffy::style::Style;
8
9/// Build a random leaf node
10fn build_random_leaf(taffy: &mut Taffy, _rng: &mut ChaCha8Rng) -> NodeId {
11 taffy.new_with_children(Style { size: points(20.0), ..Default::default() }, &[]).unwrap()
12}
13
14fn random_grid_track<R: Rng>(rng: &mut R) -> TrackSizingFunction {
15 let switch: f32 = rng.gen_range(0.0..=1.0);
16 if switch < 0.1 {
17 auto()
18 } else if switch < 0.2 {
19 min_content()
20 } else if switch < 0.3 {
21 max_content()
22 } else if switch < 0.5 {
23 fr(1.0)
24 } else if switch < 0.6 {
25 minmax(points(0.0), fr(1.0))
26 } else if switch < 0.8 {
27 points(40.0)
28 } else {
29 percent(0.3)
30 }
31}
32
33fn random_nxn_grid_style<R: Rng>(rng: &mut R, track_count: usize) -> Style {
34 Style {
35 display: Display::Grid,
36 grid_template_columns: iter::from_fn(|| Some(random_grid_track(rng))).take(track_count).collect(),
37 grid_template_rows: iter::from_fn(|| Some(random_grid_track(rng))).take(track_count).collect(),
38 ..Default::default()
39 }
40}
41
42/// A tree with many children that have shallow depth
43fn build_grid_flat_hierarchy(col_count: usize, row_count: usize) -> (Taffy, NodeId) {
44 let mut taffy = Taffy::new();
45 let mut rng = ChaCha8Rng::seed_from_u64(12345);
46
47 let style = Style {
48 display: Display::Grid,
49 grid_template_columns: iter::from_fn(|| Some(random_grid_track(&mut rng))).take(col_count).collect(),
50 grid_template_rows: iter::from_fn(|| Some(random_grid_track(&mut rng))).take(row_count).collect(),
51 ..Default::default()
52 };
53
54 let children: Vec<_> =
55 iter::from_fn(|| Some(build_random_leaf(&mut taffy, &mut rng))).take(col_count * row_count).collect();
56
57 let root = taffy.new_with_children(style, children.as_slice()).unwrap();
58 (taffy, root)
59}
60
61/// A helper function to recursively construct a deep tree
62pub fn build_deep_grid_tree(
63 tree: &mut Taffy,
64 levels: usize,
65 track_count: usize,
66 create_leaf_node: &mut impl FnMut(&mut Taffy) -> NodeId,
67 create_container_node: &mut impl FnMut(&mut Taffy, Vec<NodeId>) -> NodeId,
68) -> Vec<NodeId> {
69 // The extra one is for a position:absolute child
70 let child_count = track_count * track_count;
71
72 if levels == 1 {
73 // Build leaf nodes
74 return (0..child_count).map(|_| create_leaf_node(tree)).collect();
75 }
76
77 // Add another layer to the tree
78 // Each child gets an equal amount of the remaining nodes
79 (0..child_count)
80 .map(|_| {
81 let sub_children =
82 build_deep_grid_tree(tree, levels - 1, track_count, create_leaf_node, create_container_node);
83 create_container_node(tree, sub_children)
84 })
85 .collect()
86}
87
88/// A tree with a higher depth for a more realistic scenario
89fn build_taffy_deep_grid_hierarchy(levels: usize, track_count: usize) -> (Taffy, NodeId) {
90 let mut rng = ChaCha8Rng::seed_from_u64(12345);
91 let mut build_leaf_node = |taffy: &mut Taffy| build_random_leaf(taffy, &mut rng);
92 let mut rng = ChaCha8Rng::seed_from_u64(12345);
93 let mut build_flex_node = |taffy: &mut Taffy, children: Vec<NodeId>| {
94 taffy.new_with_children(random_nxn_grid_style(&mut rng, track_count), &children).unwrap()
95 };
96
97 let mut taffy = Taffy::new();
98 let tree = build_deep_grid_tree(&mut taffy, levels, track_count, &mut build_leaf_node, &mut build_flex_node);
99 let root = taffy.new_with_children(Style::DEFAULT, &tree).unwrap();
100 (taffy, root)
101}
102
103fn taffy_benchmarks(c: &mut Criterion) {
104 let mut group = c.benchmark_group("grid/wide");
105 group.sample_size(10);
106 for track_count in [31usize, 100, 316].iter() {
107 group.bench_with_input(
108 BenchmarkId::new(format!("{c}x{c}", c = track_count), track_count.pow(2)),
109 track_count,
110 |b, &track_count| {
111 b.iter_batched(
112 || build_grid_flat_hierarchy(track_count, track_count),
113 |(mut taffy, root)| taffy.compute_layout(root, points(12000.0)).unwrap(),
114 criterion::BatchSize::SmallInput,
115 )
116 },
117 );
118 }
119 group.finish();
120
121 let mut group = c.benchmark_group("grid/deep");
122 group.sample_size(10);
123 for (tracks, levels) in [(2, 5), (3, 4), (2, 7) /*, (3, 5)*/].iter() {
124 let children_per_level: usize = tracks * tracks;
125 group.bench_with_input(
126 BenchmarkId::new(format!("{c}x{c}", c = tracks), children_per_level.pow(*levels as u32)),
127 &(*levels, *tracks),
128 |b, &(levels, tracks)| {
129 b.iter_batched(
130 || build_taffy_deep_grid_hierarchy(levels, tracks),
131 |(mut taffy, root)| taffy.compute_layout(root, points(12000.0)).unwrap(),
132 criterion::BatchSize::SmallInput,
133 )
134 },
135 );
136 }
137 group.finish();
138
139 let mut group = c.benchmark_group("grid/superdeep");
140 group.sample_size(10);
141 for levels in [100, 1000].iter() {
142 group.bench_with_input(BenchmarkId::new("1x1", levels), levels, |b, &levels| {
143 b.iter_batched(
144 || build_taffy_deep_grid_hierarchy(levels, 1),
145 |(mut taffy, root)| taffy.compute_layout(root, max_content()).unwrap(),
146 criterion::BatchSize::SmallInput,
147 )
148 });
149 }
150 group.finish();
151}
152
153criterion_group!(benches, taffy_benchmarks);
154criterion_main!(benches);