From 291cb3179875acf190eeb53a99d24374cbd2b072 Mon Sep 17 00:00:00 2001 From: BD103 <59022059+BD103@users.noreply.github.com> Date: Sun, 29 Dec 2024 15:55:08 -0500 Subject: [PATCH] Overhaul bezier curve benchmarks (#17016) # Objective - Part of #16647. - The benchmarks for bezier curves have several issues and do not yet use the new `bench!` naming scheme. ## Solution - Make all `bevy_math` benchmarks use the `bench!` macro for their name. - Delete the `build_accel_cubic()` benchmark, since it was an exact duplicate of `build_pos_cubic()`. - Remove `collect::>()` call in `build_pos_cubic()` and replace it with a `for` loop. - Combine all of the benchmarks that measure `curve.position()` under a single group, `curve_position`, and extract the common bench routine into a helper function. - Move the time calculation for the `curve.ease()` benchmark into the setup closure so it is not tracked. - Rename the benchmarks to be more descriptive on what they do. - `easing_1000` -> `segment_ease` - `cubic_position_Vec2` -> `curve_position/vec2` - `cubic_position_Vec3A` -> `curve_position/vec3a` - `cubic_position_Vec3` -> `curve_position/vec3` - `build_pos_cubic_100_points` -> `curve_iter_positions` ## Testing - `cargo test -p benches --bench math` - `cargo bench -p benches --bench math` - Then open `./target/criterion/report/index.html` to see the report! --------- Co-authored-by: Alice Cecile --- benches/benches/bevy_math/bezier.rs | 131 ++++++++++++++-------------- 1 file changed, 67 insertions(+), 64 deletions(-) diff --git a/benches/benches/bevy_math/bezier.rs b/benches/benches/bevy_math/bezier.rs index 1858a05466..27affcaa71 100644 --- a/benches/benches/bevy_math/bezier.rs +++ b/benches/benches/bevy_math/bezier.rs @@ -1,63 +1,83 @@ +use benches::bench; +use bevy_math::{prelude::*, VectorSpace}; use core::hint::black_box; +use criterion::{ + criterion_group, measurement::Measurement, BatchSize, BenchmarkGroup, BenchmarkId, Criterion, +}; -use criterion::{criterion_group, Criterion}; +criterion_group!(benches, segment_ease, curve_position, curve_iter_positions); -use bevy_math::prelude::*; +fn segment_ease(c: &mut Criterion) { + let segment = black_box(CubicSegment::new_bezier(vec2(0.25, 0.1), vec2(0.25, 1.0))); -fn easing(c: &mut Criterion) { - let cubic_bezier = CubicSegment::new_bezier(vec2(0.25, 0.1), vec2(0.25, 1.0)); - c.bench_function("easing_1000", |b| { - b.iter(|| { - (0..1000).map(|i| i as f32 / 1000.0).for_each(|t| { - black_box(cubic_bezier.ease(black_box(t))); - }); - }); + c.bench_function(bench!("segment_ease"), |b| { + let mut t = 0; + + b.iter_batched( + || { + // Increment `t` by 1, but use modulo to constrain it to `0..=1000`. + t = (t + 1) % 1001; + + // Return time as a decimal between 0 and 1, inclusive. + t as f32 / 1000.0 + }, + |t| segment.ease(t), + BatchSize::SmallInput, + ); }); } -fn cubic_2d(c: &mut Criterion) { - let bezier = CubicBezier::new([[ +fn curve_position(c: &mut Criterion) { + /// A helper function that benchmarks calling [`CubicCurve::position()`] over a generic [`VectorSpace`]. + fn bench_curve( + group: &mut BenchmarkGroup, + name: &str, + curve: CubicCurve

, + ) { + group.bench_with_input(BenchmarkId::from_parameter(name), &curve, |b, curve| { + b.iter(|| curve.position(black_box(0.5))); + }); + } + + let mut group = c.benchmark_group(bench!("curve_position")); + + let bezier_2 = CubicBezier::new([[ vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 0.0), vec2(1.0, 1.0), ]]) .to_curve() - .expect("Unable to build a curve from this data"); - c.bench_function("cubic_position_Vec2", |b| { - b.iter(|| black_box(bezier.position(black_box(0.5)))); - }); -} + .unwrap(); -fn cubic(c: &mut Criterion) { - let bezier = CubicBezier::new([[ - vec3a(0.0, 0.0, 0.0), - vec3a(0.0, 1.0, 0.0), - vec3a(1.0, 0.0, 0.0), - vec3a(1.0, 1.0, 1.0), - ]]) - .to_curve() - .expect("Unable to build a curve from this data"); - c.bench_function("cubic_position_Vec3A", |b| { - b.iter(|| black_box(bezier.position(black_box(0.5)))); - }); -} + bench_curve(&mut group, "vec2", bezier_2); -fn cubic_vec3(c: &mut Criterion) { - let bezier = CubicBezier::new([[ + let bezier_3 = CubicBezier::new([[ vec3(0.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0), ]]) .to_curve() - .expect("Unable to build a curve from this data"); - c.bench_function("cubic_position_Vec3", |b| { - b.iter(|| black_box(bezier.position(black_box(0.5)))); - }); + .unwrap(); + + bench_curve(&mut group, "vec3", bezier_3); + + let bezier_3a = CubicBezier::new([[ + vec3a(0.0, 0.0, 0.0), + vec3a(0.0, 1.0, 0.0), + vec3a(1.0, 0.0, 0.0), + vec3a(1.0, 1.0, 1.0), + ]]) + .to_curve() + .unwrap(); + + bench_curve(&mut group, "vec3a", bezier_3a); + + group.finish(); } -fn build_pos_cubic(c: &mut Criterion) { +fn curve_iter_positions(c: &mut Criterion) { let bezier = CubicBezier::new([[ vec3a(0.0, 0.0, 0.0), vec3a(0.0, 1.0, 0.0), @@ -65,32 +85,15 @@ fn build_pos_cubic(c: &mut Criterion) { vec3a(1.0, 1.0, 1.0), ]]) .to_curve() - .expect("Unable to build a curve from this data"); - c.bench_function("build_pos_cubic_100_points", |b| { - b.iter(|| black_box(bezier.iter_positions(black_box(100)).collect::>())); + .unwrap(); + + c.bench_function(bench!("curve_iter_positions"), |b| { + b.iter(|| { + for x in bezier.iter_positions(black_box(100)) { + // Discard `x`, since we just care about `iter_positions()` being consumed, but make + // the compiler believe `x` is being used so it doesn't eliminate the iterator. + black_box(x); + } + }); }); } - -fn build_accel_cubic(c: &mut Criterion) { - let bezier = CubicBezier::new([[ - vec3a(0.0, 0.0, 0.0), - vec3a(0.0, 1.0, 0.0), - vec3a(1.0, 0.0, 0.0), - vec3a(1.0, 1.0, 1.0), - ]]) - .to_curve() - .expect("Unable to build a curve from this data"); - c.bench_function("build_accel_cubic_100_points", |b| { - b.iter(|| black_box(bezier.iter_positions(black_box(100)).collect::>())); - }); -} - -criterion_group!( - benches, - easing, - cubic_2d, - cubic_vec3, - cubic, - build_pos_cubic, - build_accel_cubic, -);