Skip to content

Commit

Permalink
Overhaul bezier curve benchmarks (bevyengine#17016)
Browse files Browse the repository at this point in the history
# Objective

- Part of bevyengine#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::<Vec<_>>()` 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 <alice.i.cecile@gmail.com>
  • Loading branch information
2 people authored and mrchantey committed Feb 4, 2025
1 parent 059364d commit 99b265e
Showing 1 changed file with 60 additions and 57 deletions.
117 changes: 60 additions & 57 deletions benches/benches/bevy_math/bezier.rs
Original file line number Diff line number Diff line change
@@ -1,96 +1,99 @@
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<M: Measurement, P: VectorSpace>(
group: &mut BenchmarkGroup<M>,
name: &str,
curve: CubicCurve<P>,
) {
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();

fn build_pos_cubic(c: &mut Criterion) {
let bezier = CubicBezier::new([[
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()
.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::<Vec<_>>()));
});
.unwrap();

bench_curve(&mut group, "vec3a", bezier_3a);

group.finish();
}

fn build_accel_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),
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::<Vec<_>>()));
.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);
}
});
});
}

criterion_group!(
benches,
easing,
cubic_2d,
cubic_vec3,
cubic,
build_pos_cubic,
build_accel_cubic,
);

0 comments on commit 99b265e

Please sign in to comment.