tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5544Rust 88Markdown 11TOML 91926 Definiciones

Add comparative benchmarks with Taffy 0.3 (#572)

* Taffy 0.3 flexbox benchmarks

* Add compute_layout method to BuildTree trait

* Simplify benchmarks with macros

c16e2b68359c50134de460348f6f02a5fb10a7e6 · por Nico Burns · (hace 2 años) · padre d4374b93f · ver el árbol en este commit

Delta semántico · 47

benches/benches/flexbox.rs

benches/src/lib.rs

benches/src/taffy_03_helpers.rs

benches/src/taffy_helpers.rs

benches/src/yoga_helpers.rs

6 archivos · +347 −185

diff --git a/benches/Cargo.toml b/benches/Cargo.toml
index b83939ce5..3baa99c7a 100644
--- a/benches/Cargo.toml
+++ b/benches/Cargo.toml
@@ -14,9 +14,10 @@ license = "MIT"
 
 [dependencies]
 criterion = "0.5"
-taffy = { path = ".." }
 rand = { version = "0.8.5" }
 rand_chacha = "0.3.1"
+taffy = { path = ".." }
+taffy_03 = { package = "taffy", version = "0.3", optional = true }
 yoga = { version = "0.4.0", optional = true }
 ordered-float = { version = "3.4.0", optional = true }
 slotmap = { version = "1.0.6", optional = true }
@@ -24,6 +25,7 @@ slotmap = { version = "1.0.6", optional = true }
 [features]
 yoga = ["dep:yoga", "dep:slotmap", "dep:ordered-float"]
 yoga-super-deep = ["yoga"]
+taffy03 = ["dep:taffy_03"]
 small = []
 large = []
 
diff --git a/benches/benches/flexbox.rs b/benches/benches/flexbox.rs
index a2c532149..25d4838b9 100644
--- a/benches/benches/flexbox.rs
+++ b/benches/benches/flexbox.rs
@@ -7,10 +7,10 @@ use taffy::style::Style as TaffyStyle;
 
 use taffy_benchmarks::{BuildTreeExt, FixedStyleGenerator, GenStyle, TaffyTreeBuilder};
 
+#[cfg(feature = "taffy03")]
+use taffy_benchmarks::taffy_03_helpers::Taffy03TreeBuilder;
 #[cfg(feature = "yoga")]
-use taffy_benchmarks::yoga_helpers;
-#[cfg(feature = "yoga")]
-use yoga_helpers::{yg, YogaTreeBuilder};
+use taffy_benchmarks::yoga_helpers::YogaTreeBuilder;
 
 fn random_dimension(rng: &mut impl Rng) -> Dimension {
     match rng.gen_range(0.0..=1.0) {
@@ -19,6 +19,8 @@ fn random_dimension(rng: &mut impl Rng) -> Dimension {
         _ => Dimension::Percent(rng.gen_range(0.0..1.0)),
     }
 }
+
+#[derive(Clone)]
 pub struct RandomStyleGenerator;
 impl GenStyle<TaffyStyle> for RandomStyleGenerator {
     fn create_leaf_style(&mut self, rng: &mut impl Rng) -> TaffyStyle {
@@ -29,112 +31,78 @@ impl GenStyle<TaffyStyle> for RandomStyleGenerator {
     }
 }
 
-/// A deep tree that matches the shape and styling that yoga use on their benchmarks
-fn build_flat_hierarchy<G: GenStyle<TaffyStyle>, TreeBuilder: BuildTreeExt<G>>(
-    target_node_count: u32,
-    style_generator: impl FnOnce() -> G,
-) -> (TreeBuilder::Tree, TreeBuilder::Node) {
-    let tree_builder = TreeBuilder::new(style_generator());
-    tree_builder.build_flat_hierarchy(target_node_count)
-}
-
-/// A deep tree that matches the shape and styling that yoga use on their benchmarks
-fn build_deep_hierarchy<G: GenStyle<TaffyStyle>, TreeBuilder: BuildTreeExt<G>>(
-    node_count: u32,
-    branching_factor: u32,
-    style_generator: impl FnOnce() -> G,
-) -> (TreeBuilder::Tree, TreeBuilder::Node) {
-    let tree_builder = TreeBuilder::new(style_generator());
-    tree_builder.build_deep_hierarchy(node_count, branching_factor)
+macro_rules! run_benchmark {
+    ($TreeBuilder: ty, $tree_builder_name: literal, $benchmark_name: expr, $group: ident, $builder: ident, $params: expr, $generate_style: expr, $generate_tree: expr) => {
+        let benchmark_id = BenchmarkId::new(format!("{} {}", $tree_builder_name, $benchmark_name), $params);
+        $group.bench_with_input(benchmark_id, $params, |b, _params| {
+            b.iter_batched(
+                || -> $TreeBuilder {
+                    let mut $builder = <$TreeBuilder>::new($generate_style());
+                    $generate_tree;
+                    $builder
+                },
+                |mut builder| builder.compute_layout(None, None),
+                criterion::BatchSize::SmallInput,
+            )
+        });
+    };
 }
 
-/// A deep tree that matches the shape and styling that yoga use on their benchmarks
-fn build_super_deep_hierarchy<G: GenStyle<TaffyStyle>, TreeBuilder: BuildTreeExt<G>>(
-    depth: u32,
-    nodes_per_level: u32,
-    style_generator: impl FnOnce() -> G,
-) -> (TreeBuilder::Tree, TreeBuilder::Node) {
-    let tree_builder = TreeBuilder::new(style_generator());
-    tree_builder.build_super_deep_hierarchy(depth, nodes_per_level)
-}
+macro_rules! benchmark_each_library {
+    ($benchmark_name: expr, $group: ident, $builder: ident, $params: expr, $generate_style: expr, $generate_tree: expr) => {
+        #[cfg(feature = "yoga")]
+        run_benchmark!(YogaTreeBuilder<_, _>, "Yoga", $benchmark_name, $group, $builder, $params, $generate_style, $generate_tree);
+        #[cfg(feature = "taffy03")]
+        run_benchmark!(Taffy03TreeBuilder<_, _>, "Taffy 0.3", $benchmark_name, $group, $builder, $params, $generate_style, $generate_tree);
 
-/// A deep tree that matches the shape and styling that yoga use on their benchmarks
-fn build_huge_nested_hierarchy<G: GenStyle<TaffyStyle>, TreeBuilder: BuildTreeExt<G>>(
-    node_count: u32,
-    branching_factor: u32,
-    style_generator: impl FnOnce() -> G,
-) -> (TreeBuilder::Tree, TreeBuilder::Node) {
-    let tree_builder = TreeBuilder::new(style_generator());
-    tree_builder.build_deep_hierarchy(node_count, branching_factor)
+        run_benchmark!(TaffyTreeBuilder<_, _>, "Taffy 0.4", $benchmark_name, $group, $builder, $params, $generate_style, $generate_tree);
+    };
 }
 
 fn huge_nested_benchmarks(c: &mut Criterion) {
-    let mut group = c.benchmark_group("yoga 'huge nested'");
-    let style = Style { size: length(10.0), flex_grow: 1.0, ..Default::default() };
-    let style_gen = || FixedStyleGenerator(style.clone());
-    for node_count in [
+    let node_counts = [
         #[cfg(feature = "small")]
         1_000u32,
         10_000,
         #[cfg(feature = "large")]
         100_000,
-    ]
-    .iter()
-    {
-        #[cfg(feature = "yoga")]
-        group.bench_with_input(BenchmarkId::new("Yoga", node_count), node_count, |b, &node_count| {
-            b.iter_batched(
-                || build_huge_nested_hierarchy::<_, YogaTreeBuilder<_, _>>(node_count, 10, style_gen),
-                |(mut tree, root)| {
-                    tree[root].calculate_layout(f32::INFINITY, f32::INFINITY, yg::Direction::LTR);
-                },
-                criterion::BatchSize::SmallInput,
-            )
-        });
-        group.bench_with_input(BenchmarkId::new("Taffy", node_count), node_count, |b, &node_count| {
-            b.iter_batched(
-                || build_huge_nested_hierarchy::<_, TaffyTreeBuilder<_, _>>(node_count, 10, style_gen),
-                |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
-                criterion::BatchSize::SmallInput,
-            )
-        });
+    ];
+
+    let mut group = c.benchmark_group("yoga 'huge nested'");
+    let style = Style { size: length(10.0), flex_grow: 1.0, ..Default::default() };
+    for node_count in node_counts.iter() {
+        benchmark_each_library!(
+            "",
+            group,
+            builder,
+            node_count,
+            || FixedStyleGenerator(style.clone()),
+            builder.build_deep_hierarchy(*node_count, 10)
+        );
     }
     group.finish();
 }
 
 fn wide_benchmarks(c: &mut Criterion) {
-    // Decrease sample size, because the tasks take longer
-    let mut group = c.benchmark_group("Wide tree");
-    group.sample_size(10);
-    for node_count in [
+    let node_counts = [
         #[cfg(feature = "small")]
         1_000u32,
         10_000,
         #[cfg(feature = "large")]
         100_000,
-    ]
-    .iter()
-    {
-        #[cfg(feature = "yoga")]
-        let benchmark_id = BenchmarkId::new(format!("Yoga (2-level hierarchy)"), node_count);
-        #[cfg(feature = "yoga")]
-        group.bench_with_input(benchmark_id, node_count, |b, &node_count| {
-            b.iter_batched(
-                || build_flat_hierarchy::<_, YogaTreeBuilder<_, _>>(node_count, || RandomStyleGenerator),
-                |(mut tree, root)| {
-                    tree[root].calculate_layout(f32::INFINITY, f32::INFINITY, yg::Direction::LTR);
-                },
-                criterion::BatchSize::SmallInput,
-            )
-        });
-        let benchmark_id = BenchmarkId::new(format!("Taffy (2-level hierarchy)"), node_count);
-        group.bench_with_input(benchmark_id, node_count, |b, &node_count| {
-            b.iter_batched(
-                || build_flat_hierarchy::<_, TaffyTreeBuilder<_, _>>(node_count, || RandomStyleGenerator),
-                |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
-                criterion::BatchSize::SmallInput,
-            )
-        });
+    ];
+
+    let mut group = c.benchmark_group("Wide tree");
+    group.sample_size(10); // Decrease sample size, because the tasks take longer
+    for node_count in node_counts.iter() {
+        benchmark_each_library!(
+            "(2-level hierarchy)",
+            group,
+            builder,
+            node_count,
+            || RandomStyleGenerator,
+            builder.build_flat_hierarchy(*node_count)
+        );
     }
     group.finish();
 }
@@ -150,64 +118,52 @@ fn deep_random_benchmarks(c: &mut Criterion) {
         (100_000, "(17-level hierarchy)"),
     ];
     for (node_count, label) in benches.iter() {
-        #[cfg(feature = "yoga")]
-        group.bench_with_input(BenchmarkId::new(format!("Yoga {label}"), node_count), node_count, |b, &node_count| {
-            b.iter_batched(
-                || build_deep_hierarchy::<_, YogaTreeBuilder<_, _>>(node_count, 2, || RandomStyleGenerator),
-                |(mut tree, root)| {
-                    tree[root].calculate_layout(f32::INFINITY, f32::INFINITY, yg::Direction::LTR);
-                },
-                criterion::BatchSize::SmallInput,
-            )
-        });
-        group.bench_with_input(BenchmarkId::new(format!("Taffy {label}"), node_count), node_count, |b, &node_count| {
-            b.iter_batched(
-                || build_deep_hierarchy::<_, TaffyTreeBuilder<_, _>>(node_count, 2, || RandomStyleGenerator),
-                |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
-                criterion::BatchSize::SmallInput,
-            )
-        });
+        benchmark_each_library!(
+            label,
+            group,
+            builder,
+            node_count,
+            || RandomStyleGenerator,
+            builder.build_deep_hierarchy(*node_count, 2)
+        );
     }
     group.finish();
 }
 
 fn deep_auto_benchmarks(c: &mut Criterion) {
-    // Decrease sample size, because the tasks take longer
-    let mut group = c.benchmark_group("Deep tree (auto size)");
-    group.sample_size(10);
-    let style = Style { flex_grow: 1.0, margin: length(10.0), ..Default::default() };
-    let style_gen = || FixedStyleGenerator(style.clone());
     let benches = [
         (4000, "(12-level hierarchy)"),
         (10_000, "(14-level hierarchy)"),
         #[cfg(feature = "large")]
         (100_000, "(17-level hierarchy)"),
     ];
+    let style = Style { flex_grow: 1.0, margin: length(10.0), ..Default::default() };
+
+    let mut group = c.benchmark_group("Deep tree (auto size)");
+    group.sample_size(10); // Decrease sample size, because the tasks take longer
     for (node_count, label) in benches.iter() {
-        #[cfg(feature = "yoga")]
-        group.bench_with_input(BenchmarkId::new(format!("Yoga {label}"), node_count), node_count, |b, &node_count| {
-            b.iter_batched(
-                || build_deep_hierarchy::<_, YogaTreeBuilder<_, _>>(node_count, 2, style_gen),
-                |(mut tree, root)| {
-                    tree[root].calculate_layout(f32::INFINITY, f32::INFINITY, yg::Direction::LTR);
-                },
-                criterion::BatchSize::SmallInput,
-            )
-        });
-        group.bench_with_input(BenchmarkId::new(format!("Taffy {label}"), node_count), node_count, |b, &node_count| {
-            b.iter_batched(
-                || build_deep_hierarchy::<_, TaffyTreeBuilder<_, _>>(node_count, 2, style_gen),
-                |(mut taffy, root)| taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(),
-                criterion::BatchSize::SmallInput,
-            )
-        });
+        benchmark_each_library!(
+            label,
+            group,
+            builder,
+            node_count,
+            || FixedStyleGenerator(style.clone()),
+            builder.build_deep_hierarchy(*node_count, 2)
+        );
     }
     group.finish();
 }
 
 fn super_deep_benchmarks(c: &mut Criterion) {
-    let mut group = c.benchmark_group("super deep");
-    group.sample_size(10);
+    let benches = [
+        #[cfg(feature = "small")]
+        50u32,
+        100,
+        #[cfg(feature = "large")]
+        200,
+    ];
+
+    #[derive(Clone)]
     struct SuperDeepStyleGen;
     impl GenStyle<TaffyStyle> for SuperDeepStyleGen {
         fn create_leaf_style(&mut self, _rng: &mut impl Rng) -> TaffyStyle {
@@ -219,40 +175,45 @@ fn super_deep_benchmarks(c: &mut Criterion) {
             self.create_leaf_style(rng)
         }
     }
-    for depth in [
-        #[cfg(feature = "small")]
-        50u32,
-        100,
-        #[cfg(feature = "large")]
-        200,
-    ]
-    .iter()
-    {
+
+    let mut group = c.benchmark_group("super deep");
+    group.sample_size(10);
+
+    for depth in benches.iter() {
         // Yoga is particularly slow at these benchmarks, so we gate them behind a separate feature flag
         #[cfg(all(feature = "yoga", feature = "yoga-super-deep"))]
-        group.bench_with_input(BenchmarkId::new("Yoga", depth), depth, |b, &depth| {
-            b.iter_batched(
-                || build_super_deep_hierarchy::<_, YogaTreeBuilder<_, _>>(depth, 3, || SuperDeepStyleGen),
-                |(mut tree, root)| {
-                    tree[root].calculate_layout(800., 800., yg::Direction::LTR);
-                },
-                criterion::BatchSize::SmallInput,
-            )
-        });
-        group.bench_with_input(BenchmarkId::new("Taffy", depth), depth, |b, &depth| {
-            b.iter_batched(
-                || build_super_deep_hierarchy::<_, TaffyTreeBuilder<_, _>>(depth, 3, || SuperDeepStyleGen),
-                |(mut taffy, root)| {
-                    taffy
-                        .compute_layout(
-                            root,
-                            Size { width: AvailableSpace::Definite(800.), height: AvailableSpace::Definite(800.) },
-                        )
-                        .unwrap()
-                },
-                criterion::BatchSize::SmallInput,
-            )
-        });
+        run_benchmark!(
+            YogaTreeBuilder<_,_>,
+            "Yoga",
+            "",
+            group,
+            builder,
+            depth,
+            || SuperDeepStyleGen,
+            builder.build_super_deep_hierarchy(*depth, 3)
+        );
+        #[cfg(feature = "taffy03")]
+        run_benchmark!(
+            Taffy03TreeBuilder<_,_>,
+            "Taffy 0.3",
+            "",
+            group,
+            builder,
+            depth,
+            || SuperDeepStyleGen,
+            builder.build_super_deep_hierarchy(*depth, 3)
+        );
+
+        run_benchmark!(
+            TaffyTreeBuilder<_,_>,
+            "Taffy 0.4",
+            "",
+            group,
+            builder,
+            depth,
+            || SuperDeepStyleGen,
+            builder.build_super_deep_hierarchy(*depth, 3)
+        );
     }
     group.finish();
 }
diff --git a/benches/src/lib.rs b/benches/src/lib.rs
index 6d67c6774..3f419540e 100644
--- a/benches/src/lib.rs
+++ b/benches/src/lib.rs
@@ -5,6 +5,9 @@
 pub mod taffy_helpers;
 pub use taffy_helpers::TaffyTreeBuilder;
 
+#[cfg(feature = "taffy03")]
+pub mod taffy_03_helpers;
+
 #[cfg(feature = "yoga")]
 pub mod yoga_helpers;
 #[cfg(feature = "yoga")]
@@ -17,7 +20,7 @@ use taffy::style::Style as TaffyStyle;
 
 pub const STANDARD_RNG_SEED: u64 = 12345;
 
-pub trait GenStyle<Style: Default> {
+pub trait GenStyle<Style: Default>: Clone {
     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 {
@@ -25,6 +28,7 @@ pub trait GenStyle<Style: Default> {
     }
 }
 
+#[derive(Clone)]
 pub struct FixedStyleGenerator(pub TaffyStyle);
 impl GenStyle<TaffyStyle> for FixedStyleGenerator {
     fn create_leaf_style(&mut self, _rng: &mut impl Rng) -> TaffyStyle {
@@ -35,12 +39,14 @@ impl GenStyle<TaffyStyle> for FixedStyleGenerator {
     }
 }
 
-pub trait BuildTree<R: Rng, G: GenStyle<TaffyStyle>>: Sized {
+pub trait BuildTree<R: Rng, G: GenStyle<TaffyStyle>> {
+    const NAME: &'static str;
     type Tree;
     type Node: Clone;
 
     fn with_rng(rng: R, style_generator: G) -> Self;
 
+    fn compute_layout_inner(&mut self, available_width: Option<f32>, available_height: Option<f32>);
     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;
@@ -69,9 +75,26 @@ pub trait BuildTree<R: Rng, G: GenStyle<TaffyStyle>>: Sized {
             })
             .collect()
     }
+}
+
+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)
+    }
 
     /// 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) {
+    fn build_super_deep_hierarchy(&mut self, depth: u32, nodes_per_level: u32) {
         let mut children = Vec::with_capacity(nodes_per_level as usize);
         for _ in 0..depth {
             let node_with_children = self.create_container_node(&children);
@@ -83,18 +106,16 @@ pub trait BuildTree<R: Rng, G: GenStyle<TaffyStyle>>: Sized {
             }
         }
         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) {
+    fn build_deep_hierarchy(&mut self, node_count: u32, branching_factor: u32) {
         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) {
+    fn build_flat_hierarchy(&mut self, target_node_count: u32) {
         let mut children = Vec::new();
 
         while self.total_node_count() < target_node_count as usize {
@@ -105,23 +126,10 @@ pub trait BuildTree<R: Rng, G: GenStyle<TaffyStyle>>: Sized {
         }
 
         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)
+    /// Compute layout given a viewport size
+    fn compute_layout(&mut self, available_width: Option<f32>, available_height: Option<f32>) {
+        self.compute_layout_inner(available_width, available_height);
     }
 }
diff --git a/benches/src/taffy_03_helpers.rs b/benches/src/taffy_03_helpers.rs
new file mode 100644
index 000000000..391e2f1f2
--- /dev/null
+++ b/benches/src/taffy_03_helpers.rs
@@ -0,0 +1,176 @@
+use rand::distributions::uniform::SampleRange;
+use rand::Rng;
+use rand_chacha::ChaCha8Rng;
+use taffy::style::Style as TaffyStyle;
+
+use super::{BuildTree, BuildTreeExt, GenStyle};
+
+pub struct Taffy03TreeBuilder<R: Rng, G: GenStyle<TaffyStyle>> {
+    rng: R,
+    style_generator: G,
+    tree: taffy_03::Taffy,
+    root: taffy_03::prelude::Node,
+}
+
+// Implement the BuildTree trait
+impl<R: Rng, G: GenStyle<TaffyStyle>> BuildTree<R, G> for Taffy03TreeBuilder<R, G> {
+    const NAME: &'static str = "Taffy 0.3";
+    type Tree = taffy_03::Taffy;
+    type Node = taffy_03::prelude::Node;
+
+    fn with_rng(mut rng: R, mut style_generator: G) -> Self {
+        let mut tree = taffy_03::Taffy::new();
+        let root = tree.new_leaf(convert_style(style_generator.create_root_style(&mut rng))).unwrap();
+        Taffy03TreeBuilder { rng, style_generator, tree, root }
+    }
+
+    fn compute_layout_inner(&mut self, available_width: Option<f32>, available_height: Option<f32>) {
+        let available_space =
+            taffy_03::geometry::Size { width: available_width.into(), height: available_height.into() };
+        self.tree.compute_layout(self.root, available_space).unwrap();
+    }
+
+    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(convert_style(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(convert_style(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 Taffy03TreeBuilder<ChaCha8Rng, G> {}
+
+fn convert_style(style: taffy::style::Style) -> taffy_03::style::Style {
+    taffy_03::style::Style {
+        display: convert_display(style.display),
+        position: convert_position(style.position),
+        inset: convert_rect(style.inset, convert_length_percentage_auto),
+        margin: convert_rect(style.margin, convert_length_percentage_auto),
+        padding: convert_rect(style.padding, convert_length_percentage),
+        border: convert_rect(style.border, convert_length_percentage),
+        size: convert_size(style.size, convert_dimension),
+        min_size: convert_size(style.min_size, convert_dimension),
+        max_size: convert_size(style.max_size, convert_dimension),
+        aspect_ratio: style.aspect_ratio,
+        gap: convert_size(style.gap, convert_length_percentage),
+        // Aligment
+        align_items: None,
+        align_self: None,
+        justify_items: None,
+        justify_self: None,
+        align_content: None,
+        justify_content: None,
+        // Flexbox
+        flex_direction: convert_flex_direction(style.flex_direction),
+        flex_wrap: convert_flex_wrap(style.flex_wrap),
+        flex_grow: style.flex_grow,
+        flex_shrink: style.flex_shrink,
+        flex_basis: convert_dimension(style.flex_basis),
+        // Grid
+        grid_template_rows: Vec::new(),
+        grid_template_columns: Vec::new(),
+        grid_auto_rows: Vec::new(),
+        grid_auto_columns: Vec::new(),
+        grid_auto_flow: taffy_03::style::GridAutoFlow::Row,
+        grid_row: taffy_03::geometry::Line {
+            start: taffy_03::style::GridPlacement::Auto,
+            end: taffy_03::style::GridPlacement::Auto,
+        },
+        grid_column: taffy_03::geometry::Line {
+            start: taffy_03::style::GridPlacement::Auto,
+            end: taffy_03::style::GridPlacement::Auto,
+        },
+    }
+}
+
+fn convert_rect<T, U, F: Fn(T) -> U>(input: taffy::geometry::Rect<T>, map: F) -> taffy_03::geometry::Rect<U> {
+    taffy_03::geometry::Rect {
+        left: map(input.left),
+        right: map(input.right),
+        top: map(input.top),
+        bottom: map(input.bottom),
+    }
+}
+
+fn convert_size<T, U, F: Fn(T) -> U>(input: taffy::geometry::Size<T>, map: F) -> taffy_03::geometry::Size<U> {
+    taffy_03::geometry::Size { width: map(input.width), height: map(input.height) }
+}
+
+fn convert_point<T, U, F: Fn(T) -> U>(input: taffy::geometry::Point<T>, map: F) -> taffy_03::geometry::Point<U> {
+    taffy_03::geometry::Point { x: map(input.x), y: map(input.y) }
+}
+
+fn convert_dimension(input: taffy::style::Dimension) -> taffy_03::style::Dimension {
+    match input {
+        taffy::style::Dimension::Length(val) => taffy_03::style::Dimension::Points(val),
+        taffy::style::Dimension::Percent(val) => taffy_03::style::Dimension::Percent(val),
+        taffy::style::Dimension::Auto => taffy_03::style::Dimension::Auto,
+    }
+}
+
+fn convert_length_percentage_auto(input: taffy::style::LengthPercentageAuto) -> taffy_03::style::LengthPercentageAuto {
+    match input {
+        taffy::style::LengthPercentageAuto::Length(val) => taffy_03::style::LengthPercentageAuto::Points(val),
+        taffy::style::LengthPercentageAuto::Percent(val) => taffy_03::style::LengthPercentageAuto::Percent(val),
+        taffy::style::LengthPercentageAuto::Auto => taffy_03::style::LengthPercentageAuto::Auto,
+    }
+}
+
+fn convert_length_percentage(input: taffy::style::LengthPercentage) -> taffy_03::style::LengthPercentage {
+    match input {
+        taffy::style::LengthPercentage::Length(val) => taffy_03::style::LengthPercentage::Points(val),
+        taffy::style::LengthPercentage::Percent(val) => taffy_03::style::LengthPercentage::Percent(val),
+    }
+}
+
+fn convert_display(input: taffy::style::Display) -> taffy_03::style::Display {
+    match input {
+        taffy::style::Display::None => taffy_03::style::Display::None,
+        taffy::style::Display::Flex => taffy_03::style::Display::Flex,
+        taffy::style::Display::Grid => taffy_03::style::Display::Grid,
+        taffy::style::Display::Block => panic!("Block layout not implemented in taffy 0.3"),
+    }
+}
+
+fn convert_position(input: taffy::style::Position) -> taffy_03::style::Position {
+    match input {
+        taffy::style::Position::Relative => taffy_03::style::Position::Relative,
+        taffy::style::Position::Absolute => taffy_03::style::Position::Absolute,
+    }
+}
+
+fn convert_flex_direction(input: taffy::style::FlexDirection) -> taffy_03::style::FlexDirection {
+    match input {
+        taffy::style::FlexDirection::Row => taffy_03::style::FlexDirection::Row,
+        taffy::style::FlexDirection::Column => taffy_03::style::FlexDirection::Column,
+        taffy::style::FlexDirection::RowReverse => taffy_03::style::FlexDirection::RowReverse,
+        taffy::style::FlexDirection::ColumnReverse => taffy_03::style::FlexDirection::ColumnReverse,
+    }
+}
+
+fn convert_flex_wrap(input: taffy::style::FlexWrap) -> taffy_03::style::FlexWrap {
+    match input {
+        taffy::style::FlexWrap::NoWrap => taffy_03::style::FlexWrap::NoWrap,
+        taffy::style::FlexWrap::Wrap => taffy_03::style::FlexWrap::Wrap,
+        taffy::style::FlexWrap::WrapReverse => taffy_03::style::FlexWrap::WrapReverse,
+    }
+}
diff --git a/benches/src/taffy_helpers.rs b/benches/src/taffy_helpers.rs
index e0fbb6b2a..0b9cebd88 100644
--- a/benches/src/taffy_helpers.rs
+++ b/benches/src/taffy_helpers.rs
@@ -16,6 +16,7 @@ pub struct TaffyTreeBuilder<R: Rng, G: GenStyle<TaffyStyle>> {
 
 // Implement the BuildTree trait
 impl<R: Rng, G: GenStyle<TaffyStyle>> BuildTree<R, G> for TaffyTreeBuilder<R, G> {
+    const NAME: &'static str = "Taffy 0.4";
     type Tree = TaffyTree;
     type Node = TaffyNodeId;
 
@@ -25,6 +26,11 @@ impl<R: Rng, G: GenStyle<TaffyStyle>> BuildTree<R, G> for TaffyTreeBuilder<R, G>
         TaffyTreeBuilder { rng, style_generator, tree, root }
     }
 
+    fn compute_layout_inner(&mut self, available_width: Option<f32>, available_height: Option<f32>) {
+        let available_space = taffy::geometry::Size { width: available_width.into(), height: available_height.into() };
+        self.tree.compute_layout(self.root, available_space).unwrap();
+    }
+
     fn random_usize(&mut self, range: impl SampleRange<usize>) -> usize {
         self.rng.gen_range(range)
     }
diff --git a/benches/src/yoga_helpers.rs b/benches/src/yoga_helpers.rs
index b05d7e61d..cd2f72229 100644
--- a/benches/src/yoga_helpers.rs
+++ b/benches/src/yoga_helpers.rs
@@ -30,6 +30,7 @@ pub struct YogaTreeBuilder<R: Rng, G: GenStyle<TaffyStyle>> {
 
 // Implement the BuildTree trait
 impl<R: Rng, G: GenStyle<TaffyStyle>> BuildTree<R, G> for YogaTreeBuilder<R, G> {
+    const NAME: &'static str = "Yoga";
     type Tree = yg::YogaTree;
     type Node = DefaultKey;
 
@@ -39,6 +40,14 @@ impl<R: Rng, G: GenStyle<TaffyStyle>> BuildTree<R, G> for YogaTreeBuilder<R, G>
         YogaTreeBuilder { rng, style_generator, tree, root }
     }
 
+    fn compute_layout_inner(&mut self, available_width: Option<f32>, available_height: Option<f32>) {
+        self.tree[self.root].calculate_layout(
+            available_width.unwrap_or(f32::INFINITY),
+            available_height.unwrap_or(f32::INFINITY),
+            yg::Direction::LTR,
+        )
+    }
+
     fn random_usize(&mut self, range: impl SampleRange<usize>) -> usize {
         self.rng.gen_range(range)
     }