Refactor/module hierarchy (#443)
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 taffy::prelude::*;
6use taffy::style::Style;
7use taffy::util::Randomizeable;
8
9mod helpers;
10use helpers::build_deep_tree;
11
12#[cfg(feature = "yoga_benchmark")]
13use helpers::yoga_helpers;
14#[cfg(feature = "yoga_benchmark")]
15use slotmap::SlotMap;
16#[cfg(feature = "yoga_benchmark")]
17use yoga_helpers::yg;
18
19/// Build a random leaf node
20fn build_random_leaf(taffy: &mut Taffy, rng: &mut ChaCha8Rng) -> NodeId {
21 taffy.new_with_children(Style::random(rng), &[]).unwrap()
22}
23
24/// A tree with many children that have shallow depth
25fn build_taffy_flat_hierarchy(total_node_count: u32) -> (Taffy, NodeId) {
26 let mut taffy = Taffy::new();
27 let mut rng = ChaCha8Rng::seed_from_u64(12345);
28 let mut children = Vec::new();
29 let mut node_count = 0;
30
31 while node_count < total_node_count {
32 let sub_children_count = rng.gen_range(1..=4);
33 let sub_children: Vec<NodeId> =
34 (0..sub_children_count).map(|_| build_random_leaf(&mut taffy, &mut rng)).collect();
35 let node = taffy.new_with_children(Style::random(&mut rng), &sub_children).unwrap();
36
37 children.push(node);
38 node_count += 1 + sub_children_count;
39 }
40
41 let root = taffy.new_with_children(Style::DEFAULT, children.as_slice()).unwrap();
42 (taffy, root)
43}
44
45#[cfg(feature = "yoga_benchmark")]
46/// A tree with many children that have shallow depth
47fn build_yoga_flat_hierarchy(total_node_count: u32) -> (yg::YogaTree, yg::NodeId) {
48 let mut tree = SlotMap::new();
49 let mut rng = ChaCha8Rng::seed_from_u64(12345);
50 let mut children = Vec::new();
51 let mut node_count = 0;
52
53 while node_count < total_node_count {
54 let sub_children_count = rng.gen_range(1..=4);
55 let sub_children: Vec<yg::NodeId> = (0..sub_children_count)
56 .map(|_| yoga_helpers::new_with_children(&mut tree, &Style::random(&mut rng), vec![]))
57 .collect();
58 let node = yoga_helpers::new_with_children(&mut tree, &Style::random(&mut rng), sub_children);
59
60 children.push(node);
61 node_count += 1 + sub_children_count;
62 }
63
64 let root = yoga_helpers::new_with_children(&mut tree, &Style::DEFAULT, children);
65 (tree, root)
66}
67
68/// A tree with a higher depth for a more realistic scenario
69fn build_taffy_deep_hierarchy(node_count: u32, branching_factor: u32) -> (Taffy, NodeId) {
70 let mut rng = ChaCha8Rng::seed_from_u64(12345);
71 let mut build_leaf_node = |taffy: &mut Taffy| build_random_leaf(taffy, &mut rng);
72 let mut rng = ChaCha8Rng::seed_from_u64(12345);
73 let mut build_flex_node =
74 |taffy: &mut Taffy, children: Vec<NodeId>| taffy.new_with_children(Style::random(&mut rng), &children).unwrap();
75
76 let mut taffy = Taffy::new();
77 let tree = build_deep_tree(&mut taffy, node_count, branching_factor, &mut build_leaf_node, &mut build_flex_node);
78 let root = taffy.new_with_children(Style::DEFAULT, &tree).unwrap();
79 (taffy, root)
80}
81
82#[cfg(feature = "yoga_benchmark")]
83/// A tree with a higher depth for a more realistic scenario
84fn build_yoga_deep_hierarchy(node_count: u32, branching_factor: u32) -> (yg::YogaTree, yg::NodeId) {
85 let mut rng = ChaCha8Rng::seed_from_u64(12345);
86 let mut build_leaf_node =
87 |tree: &mut yg::YogaTree| yoga_helpers::new_with_children(tree, &Style::random(&mut rng), vec![]);
88 let mut rng = ChaCha8Rng::seed_from_u64(12345);
89 let mut build_flex_node = |tree: &mut yg::YogaTree, children: Vec<yg::NodeId>| {
90 yoga_helpers::new_with_children(tree, &Style::random(&mut rng), children)
91 };
92
93 let mut tree = SlotMap::new();
94 let children = build_deep_tree(&mut tree, node_count, branching_factor, &mut build_leaf_node, &mut build_flex_node);
95 let root = yoga_helpers::new_with_children(&mut tree, &Style::DEFAULT, children);
96
97 (tree, root)
98}
99
100/// A deep tree that matches the shape and styling that yoga use on their benchmarks
101fn build_taffy_huge_nested_hierarchy(node_count: u32, branching_factor: u32) -> (Taffy, NodeId) {
102 let style = Style {
103 size: Size { width: Dimension::Points(10.0), height: Dimension::Points(10.0) },
104 flex_grow: 1.0,
105 ..Default::default()
106 };
107 let mut build_leaf_node = |taffy: &mut Taffy| taffy.new_leaf(style.clone()).unwrap();
108 let mut build_flex_node =
109 |taffy: &mut Taffy, children: Vec<NodeId>| taffy.new_with_children(style.clone(), &children).unwrap();
110
111 let mut taffy = Taffy::new();
112 let tree = build_deep_tree(&mut taffy, node_count, branching_factor, &mut build_leaf_node, &mut build_flex_node);
113 let root = taffy.new_with_children(Style::DEFAULT, &tree).unwrap();
114 (taffy, root)
115}
116
117#[cfg(feature = "yoga_benchmark")]
118/// A deep tree that matches the shape and styling that yoga use on their benchmarks
119fn build_yoga_huge_nested_hierarchy(node_count: u32, branching_factor: u32) -> (yg::YogaTree, yg::NodeId) {
120 let style = Style {
121 size: Size { width: Dimension::Points(10.0), height: Dimension::Points(10.0) },
122 flex_grow: 1.0,
123 ..Default::default()
124 };
125 let mut build_leaf_node = |tree: &mut yg::YogaTree| -> yg::NodeId {
126 let mut node = yg::Node::new();
127 yoga_helpers::apply_taffy_style(&mut node, &style.clone());
128 tree.insert(node)
129 };
130 let mut build_flex_node = |tree: &mut yg::YogaTree, children: Vec<yg::NodeId>| -> yg::NodeId {
131 let mut node = yg::Node::new();
132 yoga_helpers::apply_taffy_style(&mut node, &style.clone());
133 for (i, child) in children.into_iter().enumerate() {
134 node.insert_child(&mut tree[child], i as u32);
135 }
136 tree.insert(node)
137 };
138
139 let mut tree = SlotMap::new();
140 let children = build_deep_tree(&mut tree, node_count, branching_factor, &mut build_leaf_node, &mut build_flex_node);
141 let mut root = yg::Node::new();
142 for (i, child) in children.into_iter().enumerate() {
143 root.insert_child(&mut tree[child], i as u32);
144 }
145 let root = tree.insert(root);
146 (tree, root)
147}
148
149fn taffy_benchmarks(c: &mut Criterion) {
150 let mut group = c.benchmark_group("yoga 'huge nested'");
151 for node_count in [1_000u32, 10_000, 100_000].iter() {
152 #[cfg(feature = "yoga_benchmark")]
153 group.bench_with_input(BenchmarkId::new("Yoga", node_count), node_count, |b, &node_count| {
154 b.iter_batched(
155 || build_yoga_huge_nested_hierarchy(node_count, 10),
156 |(mut tree, root)| {
157 tree[root].calculate_layout(f32::INFINITY, f32::INFINITY, yg::Direction::LTR);
158 },
159 criterion::BatchSize::SmallInput,
160 )
161 });
162 group.bench_with_input(BenchmarkId::new("Taffy", node_count), node_count, |b, &node_count| {
163 b.iter_batched(
164 || build_taffy_huge_nested_hierarchy(node_count, 10),
165 |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
166 criterion::BatchSize::SmallInput,
167 )
168 });
169 }
170 group.finish();
171
172 // Decrease sample size, because the tasks take longer
173 let mut group = c.benchmark_group("big trees (wide)");
174 group.sample_size(10);
175 for node_count in [1_000u32, 10_000, 100_000].iter() {
176 #[cfg(feature = "yoga_benchmark")]
177 let benchmark_id = BenchmarkId::new(format!("Yoga (2-level hierarchy)"), node_count);
178 #[cfg(feature = "yoga_benchmark")]
179 group.bench_with_input(benchmark_id, node_count, |b, &node_count| {
180 b.iter_batched(
181 || build_yoga_flat_hierarchy(node_count),
182 |(mut tree, root)| {
183 tree[root].calculate_layout(f32::INFINITY, f32::INFINITY, yg::Direction::LTR);
184 },
185 criterion::BatchSize::SmallInput,
186 )
187 });
188 let benchmark_id = BenchmarkId::new(format!("Taffy (2-level hierarchy)"), node_count);
189 group.bench_with_input(benchmark_id, node_count, |b, &node_count| {
190 b.iter_batched(
191 || build_taffy_flat_hierarchy(node_count),
192 |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
193 criterion::BatchSize::SmallInput,
194 )
195 });
196 }
197 group.finish();
198
199 // Decrease sample size, because the tasks take longer
200 let mut group = c.benchmark_group("big trees (deep)");
201 group.sample_size(10);
202 let benches = [(4000, "(12-level hierarchy)"), (10_000, "(14-level hierarchy)"), (100_000, "(17-level hierarchy)")];
203 for (node_count, label) in benches.iter() {
204 #[cfg(feature = "yoga_benchmark")]
205 group.bench_with_input(BenchmarkId::new(format!("Yoga {label}"), node_count), node_count, |b, &node_count| {
206 b.iter_batched(
207 || build_yoga_deep_hierarchy(node_count, 2),
208 |(mut tree, root)| {
209 tree[root].calculate_layout(f32::INFINITY, f32::INFINITY, yg::Direction::LTR);
210 },
211 criterion::BatchSize::SmallInput,
212 )
213 });
214 group.bench_with_input(BenchmarkId::new(format!("Taffy {label}"), node_count), node_count, |b, &node_count| {
215 b.iter_batched(
216 || build_taffy_deep_hierarchy(node_count, 2),
217 |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
218 criterion::BatchSize::SmallInput,
219 )
220 });
221 }
222 group.finish();
223
224 let mut group = c.benchmark_group("super deep (1000-level hierarchy)");
225 group.sample_size(10);
226 for node_count in [1000u32].iter() {
227 #[cfg(feature = "yoga_benchmark")]
228 group.bench_with_input(BenchmarkId::new("Yoga", node_count), node_count, |b, &node_count| {
229 b.iter_batched(
230 || build_yoga_deep_hierarchy(node_count, 2),
231 |(mut tree, root)| {
232 tree[root].calculate_layout(f32::INFINITY, f32::INFINITY, yg::Direction::LTR);
233 },
234 criterion::BatchSize::SmallInput,
235 )
236 });
237 group.bench_with_input(BenchmarkId::new("Taffy", node_count), node_count, |b, &node_count| {
238 b.iter_batched(
239 || build_taffy_deep_hierarchy(node_count, 2),
240 |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
241 criterion::BatchSize::SmallInput,
242 )
243 });
244 }
245 group.finish();
246}
247
248criterion_group!(benches, taffy_benchmarks);
249criterion_main!(benches);