Fix benchmark measurement (#279)
* Run setup for every iteration of the benchmark
* Fix clippy inline formatting warning
* Cargo fmt
* Update README with corrected results
* Reinstate 1_000_000 node benchmark
* Rustfmt
* Update benchmark results
* Update benchmark description
fb07aa8a6de7135b57ec4a440b86cfe10ffcdbd0 · by Nico Burns · (3 years ago) · parent 5c7896577 · browse the tree at this commit
Semantic delta · 4
scripts/gentest/src/main.rs
diff --git a/README.md b/README.md
index 9459adbab..b57bdea2d 100644
--- a/README.md
+++ b/README.md
@@ -76,11 +76,11 @@ assert_eq!(taffy.layout(body_node).unwrap().size.height, 500.0); // This value w
| Benchmark | Yoga | Taffy 0.2 |
| --- | --- | --- |
-| yoga/10 nodes (1-level hierarchy) | 45.1670 µs | 33.297 ns |
-| yoga/100 nodes (2-level hierarchy) | 134.1250 µs | 336.53 ns |
-| yoga/1_000 nodes (3-level hierarchy) | 1.2221 ms | 3.8928 µs |
-| yoga/10_000 nodes (4-level hierarchy) | 13.8672 ms | 36.162 µs |
-| yoga/100_000 nodes (5-level hierarchy) | 141.5307 ms | 1.6404 ms |
+| yoga/10 nodes (1-level hierarchy) | 45.1670 µs | 1.9857 µs |
+| yoga/100 nodes (2-level hierarchy) | 134.1250 µs | 41.810 µs |
+| yoga/1_000 nodes (3-level hierarchy) | 1.2221 ms | 357.48 µs |
+| yoga/10_000 nodes (4-level hierarchy) | 13.8672 ms | 3.7310 ms |
+| yoga/100_000 nodes (5-level hierarchy) | 141.5307 ms | 39.682 ms |
Most popular websites seem to have between 3,000 and 10,000 nodes (although they also require text layout, which neither yoga nor taffy implement).
diff --git a/benches/big_tree.rs b/benches/big_tree.rs
index 12154205a..6c583deec 100644
--- a/benches/big_tree.rs
+++ b/benches/big_tree.rs
@@ -88,39 +88,75 @@ fn taffy_benchmarks(c: &mut Criterion) {
group.sample_size(10);
group.bench_function("10 nodes", |b| {
- let mut taffy = Taffy::new();
- let root = build_yoga_deep_hierarchy(&mut taffy, 10, 10);
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_yoga_deep_hierarchy(&mut taffy, 10, 10);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::SmallInput,
+ )
});
group.bench_function("100 nodes", |b| {
- let mut taffy = Taffy::new();
- let root = build_yoga_deep_hierarchy(&mut taffy, 100, 10);
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_yoga_deep_hierarchy(&mut taffy, 100, 10);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::SmallInput,
+ )
});
group.bench_function("1_000 nodes", |b| {
- let mut taffy = Taffy::new();
- let root = build_yoga_deep_hierarchy(&mut taffy, 1_000, 10);
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_yoga_deep_hierarchy(&mut taffy, 1_000, 10);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::SmallInput,
+ )
});
group.bench_function("10_000 nodes", |b| {
- let mut taffy = Taffy::new();
- let root = build_yoga_deep_hierarchy(&mut taffy, 10_000, 10);
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_yoga_deep_hierarchy(&mut taffy, 10_000, 10);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::SmallInput,
+ )
});
group.bench_function("100_000 nodes", |b| {
- let mut taffy = Taffy::new();
- let root = build_yoga_deep_hierarchy(&mut taffy, 100_000, 10);
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_yoga_deep_hierarchy(&mut taffy, 100_000, 10);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::LargeInput,
+ )
});
group.bench_function("1_000_000 nodes", |b| {
- let mut taffy = Taffy::new();
- let root = build_yoga_deep_hierarchy(&mut taffy, 1_000_000, 10);
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_yoga_deep_hierarchy(&mut taffy, 1_000_000, 10);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::LargeInput,
+ )
});
drop(group);
@@ -129,45 +165,75 @@ fn taffy_benchmarks(c: &mut Criterion) {
group.sample_size(10);
group.bench_function("10 nodes (2-level hierarchy)", |b| {
- let mut taffy = Taffy::new();
- let root = build_flat_hierarchy(&mut taffy, 10);
-
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_flat_hierarchy(&mut taffy, 10);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::SmallInput,
+ )
});
group.bench_function("100 nodes (2-level hierarchy)", |b| {
- let mut taffy = Taffy::new();
- let root = build_flat_hierarchy(&mut taffy, 100);
-
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_flat_hierarchy(&mut taffy, 100);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::SmallInput,
+ )
});
group.bench_function("1_000 nodes (2-level hierarchy)", |b| {
- let mut taffy = Taffy::new();
- let root = build_flat_hierarchy(&mut taffy, 1_000);
-
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_flat_hierarchy(&mut taffy, 1_000);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::SmallInput,
+ )
});
group.bench_function("10_000 nodes (2-level hierarchy)", |b| {
- let mut taffy = Taffy::new();
- let root = build_flat_hierarchy(&mut taffy, 10_000);
-
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_flat_hierarchy(&mut taffy, 10_000);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::LargeInput,
+ )
});
group.bench_function("100_000 nodes (2-level hierarchy)", |b| {
- let mut taffy = Taffy::new();
- let root = build_flat_hierarchy(&mut taffy, 100_000);
-
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_flat_hierarchy(&mut taffy, 100_000);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::LargeInput,
+ )
});
group.bench_function("100_000 nodes (7-level hierarchy)", |b| {
- let mut taffy = Taffy::new();
- let root = build_deep_hierarchy(&mut taffy, 100_000, 7);
-
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_deep_hierarchy(&mut taffy, 100_000, 7);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::LargeInput,
+ )
});
drop(group);
@@ -177,31 +243,51 @@ fn taffy_benchmarks(c: &mut Criterion) {
group.sample_size(10);
group.bench_function("4000 nodes (12-level hierarchy)", |b| {
- let mut taffy = Taffy::new();
- let root = build_deep_hierarchy(&mut taffy, 4000, 2);
-
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_deep_hierarchy(&mut taffy, 4000, 2);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::SmallInput,
+ )
});
group.bench_function("10_000 nodes (14-level hierarchy)", |b| {
- let mut taffy = Taffy::new();
- let root = build_deep_hierarchy(&mut taffy, 10_000, 2);
-
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_deep_hierarchy(&mut taffy, 10_000, 2);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::SmallInput,
+ )
});
group.bench_function("100_000 nodes (17-level hierarchy)", |b| {
- let mut taffy = Taffy::new();
- let root = build_deep_hierarchy(&mut taffy, 100_000, 2);
-
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_deep_hierarchy(&mut taffy, 100_000, 2);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::LargeInput,
+ )
});
group.bench_function("1_000_000 nodes (20-level hierarchy)", |b| {
- let mut taffy = Taffy::new();
- let root = build_deep_hierarchy(&mut taffy, 1_000_000, 2);
-
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_deep_hierarchy(&mut taffy, 1_000_000, 2);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::LargeInput,
+ )
});
drop(group);
@@ -210,15 +296,27 @@ fn taffy_benchmarks(c: &mut Criterion) {
group.sample_size(10);
group.bench_function("100 nodes (100-level hierarchy)", |b| {
- let mut taffy = Taffy::new();
- let root = build_yoga_deep_hierarchy(&mut taffy, 100, 1);
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_deep_hierarchy(&mut taffy, 100, 1);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::SmallInput,
+ )
});
group.bench_function("1_000 nodes (1000-level hierarchy)", |b| {
- let mut taffy = Taffy::new();
- let root = build_yoga_deep_hierarchy(&mut taffy, 1_000, 1);
- b.iter(|| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap())
+ b.iter_batched(
+ || {
+ let mut taffy = Taffy::new();
+ let root = build_deep_hierarchy(&mut taffy, 1_000, 1);
+ (taffy, root)
+ },
+ |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
+ criterion::BatchSize::SmallInput,
+ )
});
}
diff --git a/benches/results-2022-11-23.md b/benches/results-2022-11-23.md
deleted file mode 100644
index b00cff9fe..000000000
--- a/benches/results-2022-11-23.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# Benchmarks (Taffy 0.2 vs Taffy 0.1 and Yoga)
-
-- Using [this branch](https://github.com/nicoburns/taffy/tree/0.1-benchmarks) that backports the new benchmarks to the `0.1` tag.
-- Run on a 2021 MacBook Pro with M1 Pro processor.
-- Taffy benchmarks are using criterion (10 iterations).
-- Yoga benchmarks run via the `yoga-layout-prebuilt` npm package, they were run a few times manually and it was verified that variance in the numbers of each run was minimal. It should be noted that this is using an old version of Yoga.
-
-| Benchmark | Yoga | Taffy 0.1 | Taffy 0.2 | % change (0.1 -> 0.2) |
-| --- | --- | --- | --- | --- |
-| yoga/10 nodes (1-level hierarchy) | 45.1670 µs | 68.499 ns | 33.297 ns | -51.161% |
-| yoga/100 nodes (2-level hierarchy) | 134.1250 µs | 378.22 ns | 336.53 ns | -10.862% |
-| yoga/1_000 nodes (3-level hierarchy) | 1.2221 ms | 3.2015 µs | 3.8928 µs | +21.080% |
-| yoga/10_000 nodes (4-level hierarchy) | 13.8672 ms | 37.347 µs | 36.162 µs | -11.971% |
-| yoga/100_000 nodes (5-level hierarchy) | 141.5307 ms | 1.5660 s | 1.6404 ms | -99.890% |
-| yoga /1_000_000 nodes (6-level hierarchy) | - | - | 50.760 ms | - |
-| wide/10 nodes (2-level hierarchy) | - | 73.037 ns | 41.936 ns | -41.936% |
-| wide/100 nodes (2-level hierarchy) | - | 371.70 ns | 309.37 ns | -16.605% |
-| wide/1_000 nodes (2-level hierarchy) | - | 3.5458 µs | 4.3571 µs | +23.333% |
-| wide/10_000 nodes (2-level hierarchy) | - | 36.418 µs | 42.967 µs | +17.357% |
-| wide/100_000 nodes (2-level hierarchy) | - | 1.8275 ms | 3.9096 ms | +112.26% |
-| semi-wide/100_000 nodes (7-level hierarchy) | - | 2.6569 s | 1.4992 ms | -99.933% |
-| deep/4000 nodes (12-level hierarchy)) | - | 5.1845 s | 15.318 µs | -100.000% |
-| deep/10_000 nodes (14-level hierarchy) | - | 75.978 s | 40.315 µs | -100.000% |
-| deep/100_000 nodes (17-level hierarchy) | - | - | 2.7644 ms| - |
-| deep/1_000_000 nodes (20-level hierarchy) | - | - | 1.2130 s| - |
-| deep hierarchy/build | - | 1.4869 µs | 713.45 ns | -4.4346% |
-| deep hierarchy/single | - | 7.1058 µs | 4.6564 µs | -2.0999% |
-| deep hierarchy/relayout | - | 3.7909 µs | 2.1306 µs | -2.4861% |
-| generated benchmarks | - | 256.80 µs | 218.71 µs | -14.485% |
diff --git a/benches/results-2022-12-07.md b/benches/results-2022-12-07.md
new file mode 100644
index 000000000..cec319998
--- /dev/null
+++ b/benches/results-2022-12-07.md
@@ -0,0 +1,32 @@
+# Benchmarks (Taffy 0.2 vs Taffy 0.1 and Yoga)
+
+- Taffy `0.1` results come from commit `52794ddf23dea463fcad5fb73db4f8362b894866`
+- Taffy `0.2` results come from commit `44763a9e74e87423f3158014d6d06c762ce44d19`
+- Run on a 2021 MacBook Pro with M1 Pro processor.
+- Taffy benchmarks are using criterion (10 iterations).
+- Yoga benchmarks run via the `yoga-layout-prebuilt` npm package, they were run a few times manually and it was verified that variance in the numbers of each run was minimal. It should be noted that this is using an old version of Yoga.
+
+| Benchmark | Yoga | Taffy 0.1 | Taffy 0.2 | % change (0.1 -> 0.2) |
+| --- | --- | --- | --- | --- |
+| yoga/10 nodes (1-level hierarchy) | 45.1670 µs | 2.2851 µs | 2.0787 µs | -9.3338% |
+| yoga/100 nodes (2-level hierarchy) | 134.1250 µs | 75.518 µs | 41.022 µs | -46.577% |
+| yoga/1_000 nodes (3-level hierarchy) | 1.2221 ms | 1.7794 ms | 355.50 µs | -79.608% |
+| yoga/10_000 nodes (4-level hierarchy) | 13.8672 ms | 52.117 ms | 3.6848 ms | -92.968% |
+| yoga/100_000 nodes (5-level hierarchy) | 141.5307 ms | 1.5626 s | 39.830 ms | -97.459% |
+| yoga /1_000_000 nodes (6-level hierarchy) | - | - | 384.19 ms | - |
+| wide/10 nodes (2-level hierarchy) | - | 6.1508 µs | 4.1094 µs | -33.923% |
+| wide/100 nodes (2-level hierarchy) | - | 66.602 µs | 41.056 µs | -35.742% |
+| wide/1_000 nodes (2-level hierarchy) | - | 699.18 µs | 445.01 µs | -36.279% |
+| wide/10_000 nodes (2-level hierarchy) | - | 8.8244 ms | 7.1313 ms | -16.352% |
+| wide/100_000 nodes (2-level hierarchy) | - | 204.48 ms | 242.93 ms | +18.803% |
+| semi-wide/100_000 nodes (7-level hierarchy) | - | 2.6539 s | 69.141 ms | -97.402% |
+| deep/4000 nodes (12-level hierarchy)) | - | 5.2320 s | 2.7363 ms | -99.947% |
+| deep/10_000 nodes (14-level hierarchy) | - | 75.207 s | 6.9415 ms | -99.991% |
+| deep/100_000 nodes (17-level hierarchy) | - | - | 102.72 ms| - |
+| deep/1_000_000 nodes (20-level hierarchy) | - | - | 799.35 ms | - |
+| super-deep/100 nodes (100-level hierarchy) | - | - | 111.26 µs | - |
+| super-deep/1000 nodes (1000-level hierarchy) | - | - | 1.1746 ms | - |
+| deep hierarchy/build | - | 1.5060 µs | 616.12 ns | -1.5167% |
+| deep hierarchy/single | - | 7.1453 µs | 4.5461 µs | -0.5156% |
+| deep hierarchy/relayout | - | 3.7863 µs | 2.1275 µs | -1.0900% |
+| generated benchmarks | - | 255.83 µs | 221.73 µs | -13.125% |
diff --git a/scripts/gentest/src/main.rs b/scripts/gentest/src/main.rs
index 41d6c0f70..75a7a1716 100644
--- a/scripts/gentest/src/main.rs
+++ b/scripts/gentest/src/main.rs
@@ -201,7 +201,7 @@ fn generate_assertions(ident: &str, node: &json::JsonValue) -> TokenStream {
let mut c = Vec::new();
if let json::JsonValue::Array(ref value) = node["children"] {
for (idx, child) in value.iter().enumerate() {
- c.push(generate_assertions(&format!("{}{}", ident, idx), child));
+ c.push(generate_assertions(&format!("{ident}{idx}"), child));
}
};
c.into_iter().fold(quote!(), |a, b| quote!(#a #b))
@@ -468,15 +468,12 @@ fn generate_node(ident: &str, node: &json::JsonValue) -> TokenStream {
let (children_body, children) = match node["children"] {
json::JsonValue::Array(ref value) => {
if !value.is_empty() {
- let body = value
- .iter()
- .enumerate()
- .map(|(i, child)| generate_node(&format!("{}{}", ident, i), child))
- .collect();
+ let body =
+ value.iter().enumerate().map(|(i, child)| generate_node(&format!("{ident}{i}"), child)).collect();
let idents = value
.iter()
.enumerate()
- .map(|(i, _)| Ident::new(&format!("{}{}", ident, i), Span::call_site()))
+ .map(|(i, _)| Ident::new(&format!("{ident}{i}"), Span::call_site()))
.collect::<Vec<_>>();
(body, quote!(&[#(#idents),*]))
} else {
diff --git a/src/error.rs b/src/error.rs
index 88cef3bea..57764743b 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -31,16 +31,14 @@ pub enum TaffyError {
impl Display for TaffyError {
fn fmt(&self, f: &mut Formatter) -> Result {
match self {
- TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count } => write!(
- f,
- "Index (is {}) should be < child_count ({}) for parent node {:?}",
- child_index, child_count, parent
- ),
+ TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count } => {
+ write!(f, "Index (is {child_index}) should be < child_count ({child_count}) for parent node {parent:?}")
+ }
TaffyError::InvalidParentNode(parent) => {
- write!(f, "Parent Node {:?} is not in the Taffy instance", parent)
+ write!(f, "Parent Node {parent:?} is not in the Taffy instance")
}
- TaffyError::InvalidChildNode(child) => write!(f, "Child Node {:?} is not in the Taffy instance", child),
- TaffyError::InvalidInputNode(node) => write!(f, "Supplied Node {:?} is not in the Taffy instance", node),
+ TaffyError::InvalidChildNode(child) => write!(f, "Child Node {child:?} is not in the Taffy instance"),
+ TaffyError::InvalidInputNode(node) => write!(f, "Supplied Node {node:?} is not in the Taffy instance"),
}
}
}