diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 2ed96d3a48..db2a5b133e 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -7,6 +7,11 @@ license = "MIT OR Apache-2.0" # Do not automatically discover benchmarks, we specify them manually instead. autobenches = false +[dependencies] +# The primary crate that runs and analyzes our benchmarks. This is a regular dependency because the +# `bench!` macro refers to it in its documentation. +criterion = { version = "0.5.1", features = ["html_reports"] } + [dev-dependencies] # Bevy crates bevy_app = { path = "../crates/bevy_app" } @@ -22,7 +27,6 @@ bevy_tasks = { path = "../crates/bevy_tasks" } bevy_utils = { path = "../crates/bevy_utils" } # Other crates -criterion = { version = "0.5.1", features = ["html_reports"] } glam = "0.29" rand = "0.8" rand_chacha = "0.3" diff --git a/benches/benches/bevy_reflect/function.rs b/benches/benches/bevy_reflect/function.rs index 5398cc3da9..77dee4f118 100644 --- a/benches/benches/bevy_reflect/function.rs +++ b/benches/benches/bevy_reflect/function.rs @@ -1,14 +1,25 @@ -use bevy_reflect::func::{ArgList, IntoFunction, IntoFunctionMut, TypedFunction}; -use criterion::{criterion_group, BatchSize, Criterion}; +use core::hint::black_box; -criterion_group!(benches, typed, into, call, overload, clone); +use benches::bench; +use bevy_reflect::func::{ArgList, IntoFunction, IntoFunctionMut, TypedFunction}; +use criterion::{criterion_group, BatchSize, BenchmarkId, Criterion}; + +criterion_group!( + benches, + typed, + into, + call, + clone, + with_overload, + call_overload, +); fn add(a: i32, b: i32) -> i32 { a + b } fn typed(c: &mut Criterion) { - c.benchmark_group("typed") + c.benchmark_group(bench!("typed")) .bench_function("function", |b| { b.iter(|| add.get_function_info()); }) @@ -25,7 +36,7 @@ fn typed(c: &mut Criterion) { } fn into(c: &mut Criterion) { - c.benchmark_group("into") + c.benchmark_group(bench!("into")) .bench_function("function", |b| { b.iter(|| add.into_function()); }) @@ -36,17 +47,18 @@ fn into(c: &mut Criterion) { }) .bench_function("closure_mut", |b| { let mut _capture = 25; + // `move` is required here because `into_function_mut()` takes ownership of `self`. let closure = move |a: i32| _capture += a; b.iter(|| closure.into_function_mut()); }); } fn call(c: &mut Criterion) { - c.benchmark_group("call") + c.benchmark_group(bench!("call")) .bench_function("trait_object", |b| { b.iter_batched( || Box::new(add) as Box i32>, - |func| func(75, 25), + |func| func(black_box(75), black_box(25)), BatchSize::SmallInput, ); }) @@ -78,35 +90,43 @@ fn call(c: &mut Criterion) { }); } -fn overload(c: &mut Criterion) { - fn add>(a: T, b: T) -> T { - a + b - } +fn clone(c: &mut Criterion) { + c.benchmark_group(bench!("clone")) + .bench_function("function", |b| { + let add = add.into_function(); + b.iter(|| add.clone()); + }); +} - #[expect(clippy::too_many_arguments)] - fn complex( - _: T0, - _: T1, - _: T2, - _: T3, - _: T4, - _: T5, - _: T6, - _: T7, - _: T8, - _: T9, - ) { - } +fn simple>(a: T, b: T) -> T { + a + b +} - c.benchmark_group("with_overload") - .bench_function("01_simple_overload", |b| { +#[expect(clippy::too_many_arguments)] +fn complex( + _: T0, + _: T1, + _: T2, + _: T3, + _: T4, + _: T5, + _: T6, + _: T7, + _: T8, + _: T9, +) { +} + +fn with_overload(c: &mut Criterion) { + c.benchmark_group(bench!("with_overload")) + .bench_function(BenchmarkId::new("simple_overload", 1), |b| { b.iter_batched( - || add::.into_function(), - |func| func.with_overload(add::), + || simple::.into_function(), + |func| func.with_overload(simple::), BatchSize::SmallInput, ); }) - .bench_function("01_complex_overload", |b| { + .bench_function(BenchmarkId::new("complex_overload", 1), |b| { b.iter_batched( || complex::.into_function(), |func| { @@ -115,18 +135,18 @@ fn overload(c: &mut Criterion) { BatchSize::SmallInput, ); }) - .bench_function("03_simple_overload", |b| { + .bench_function(BenchmarkId::new("simple_overload", 3), |b| { b.iter_batched( - || add::.into_function(), + || simple::.into_function(), |func| { - func.with_overload(add::) - .with_overload(add::) - .with_overload(add::) + func.with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::) }, BatchSize::SmallInput, ); }) - .bench_function("03_complex_overload", |b| { + .bench_function(BenchmarkId::new("complex_overload", 3), |b| { b.iter_batched( || complex::.into_function(), |func| { @@ -137,24 +157,24 @@ fn overload(c: &mut Criterion) { BatchSize::SmallInput, ); }) - .bench_function("10_simple_overload", |b| { + .bench_function(BenchmarkId::new("simple_overload", 10), |b| { b.iter_batched( - || add::.into_function(), + || simple::.into_function(), |func| { - func.with_overload(add::) - .with_overload(add::) - .with_overload(add::) - .with_overload(add::) - .with_overload(add::) - .with_overload(add::) - .with_overload(add::) - .with_overload(add::) - .with_overload(add::) + func.with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::) }, BatchSize::SmallInput, ); }) - .bench_function("10_complex_overload", |b| { + .bench_function(BenchmarkId::new("complex_overload", 10), |b| { b.iter_batched( || complex::.into_function(), |func| { @@ -171,41 +191,41 @@ fn overload(c: &mut Criterion) { BatchSize::SmallInput, ); }) - .bench_function("01_nested_simple_overload", |b| { + .bench_function(BenchmarkId::new("nested_simple_overload", 1), |b| { b.iter_batched( - || add::.into_function(), - |func| func.with_overload(add::), + || simple::.into_function(), + |func| func.with_overload(simple::), BatchSize::SmallInput, ); }) - .bench_function("03_nested_simple_overload", |b| { + .bench_function(BenchmarkId::new("nested_simple_overload", 3), |b| { b.iter_batched( - || add::.into_function(), + || simple::.into_function(), |func| { func.with_overload( - add:: - .into_function() - .with_overload(add::.into_function().with_overload(add::)), + simple::.into_function().with_overload( + simple::.into_function().with_overload(simple::), + ), ) }, BatchSize::SmallInput, ); }) - .bench_function("10_nested_simple_overload", |b| { + .bench_function(BenchmarkId::new("nested_simple_overload", 10), |b| { b.iter_batched( - || add::.into_function(), + || simple::.into_function(), |func| { func.with_overload( - add::.into_function().with_overload( - add::.into_function().with_overload( - add::.into_function().with_overload( - add::.into_function().with_overload( - add::.into_function().with_overload( - add::.into_function().with_overload( - add::.into_function().with_overload( - add:: + simple::.into_function().with_overload( + simple::.into_function().with_overload( + simple::.into_function().with_overload( + simple::.into_function().with_overload( + simple::.into_function().with_overload( + simple::.into_function().with_overload( + simple::.into_function().with_overload( + simple:: .into_function() - .with_overload(add::), + .with_overload(simple::), ), ), ), @@ -218,13 +238,15 @@ fn overload(c: &mut Criterion) { BatchSize::SmallInput, ); }); +} - c.benchmark_group("call_overload") - .bench_function("01_simple_overload", |b| { +fn call_overload(c: &mut Criterion) { + c.benchmark_group(bench!("call_overload")) + .bench_function(BenchmarkId::new("simple_overload", 1), |b| { b.iter_batched( || { ( - add::.into_function().with_overload(add::), + simple::.into_function().with_overload(simple::), ArgList::new().push_owned(75_i8).push_owned(25_i8), ) }, @@ -232,7 +254,7 @@ fn overload(c: &mut Criterion) { BatchSize::SmallInput, ); }) - .bench_function("01_complex_overload", |b| { + .bench_function(BenchmarkId::new("complex_overload", 1), |b| { b.iter_batched( || { ( @@ -258,15 +280,15 @@ fn overload(c: &mut Criterion) { BatchSize::SmallInput, ); }) - .bench_function("03_simple_overload", |b| { + .bench_function(BenchmarkId::new("simple_overload", 3), |b| { b.iter_batched( || { ( - add:: + simple:: .into_function() - .with_overload(add::) - .with_overload(add::) - .with_overload(add::), + .with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::), ArgList::new().push_owned(75_i32).push_owned(25_i32), ) }, @@ -274,7 +296,7 @@ fn overload(c: &mut Criterion) { BatchSize::SmallInput, ); }) - .bench_function("03_complex_overload", |b| { + .bench_function(BenchmarkId::new("complex_overload", 3), |b| { b.iter_batched( || { ( @@ -306,21 +328,21 @@ fn overload(c: &mut Criterion) { BatchSize::SmallInput, ); }) - .bench_function("10_simple_overload", |b| { + .bench_function(BenchmarkId::new("simple_overload", 10), |b| { b.iter_batched( || { ( - add:: + simple:: .into_function() - .with_overload(add::) - .with_overload(add::) - .with_overload(add::) - .with_overload(add::) - .with_overload(add::) - .with_overload(add::) - .with_overload(add::) - .with_overload(add::) - .with_overload(add::), + .with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::) + .with_overload(simple::), ArgList::new().push_owned(75_u8).push_owned(25_u8), ) }, @@ -328,7 +350,7 @@ fn overload(c: &mut Criterion) { BatchSize::SmallInput, ); }) - .bench_function("10_complex_overload", |b| { + .bench_function(BenchmarkId::new("complex_overload", 10), |b| { b.iter_batched( || { ( @@ -379,10 +401,3 @@ fn overload(c: &mut Criterion) { ); }); } - -fn clone(c: &mut Criterion) { - c.benchmark_group("clone").bench_function("function", |b| { - let add = add.into_function(); - b.iter(|| add.clone()); - }); -} diff --git a/benches/benches/bevy_reflect/list.rs b/benches/benches/bevy_reflect/list.rs index d9c92dd03e..dca727594f 100644 --- a/benches/benches/bevy_reflect/list.rs +++ b/benches/benches/bevy_reflect/list.rs @@ -1,9 +1,10 @@ use core::{iter, time::Duration}; +use benches::bench; use bevy_reflect::{DynamicList, List}; use criterion::{ - black_box, criterion_group, measurement::Measurement, BatchSize, BenchmarkGroup, BenchmarkId, - Criterion, Throughput, + black_box, criterion_group, measurement::Measurement, AxisScale, BatchSize, BenchmarkGroup, + BenchmarkId, Criterion, PlotConfiguration, Throughput, }; criterion_group!( @@ -14,11 +15,29 @@ criterion_group!( dynamic_list_push ); +// Use a shorter warm-up time (from 3 to 0.5 seconds) and measurement time (from 5 to 4) because we +// have so many combinations (>50) to benchmark. const WARM_UP_TIME: Duration = Duration::from_millis(500); const MEASUREMENT_TIME: Duration = Duration::from_secs(4); -// log10 scaling -const SIZES: [usize; 5] = [100_usize, 316, 1000, 3162, 10000]; +/// An array of list sizes used in benchmarks. +/// +/// This scales logarithmically. +const SIZES: [usize; 5] = [100, 316, 1000, 3162, 10000]; + +/// Creates a [`BenchmarkGroup`] with common configuration shared by all benchmarks within this +/// module. +fn create_group<'a, M: Measurement>(c: &'a mut Criterion, name: &str) -> BenchmarkGroup<'a, M> { + let mut group = c.benchmark_group(name); + + group + .warm_up_time(WARM_UP_TIME) + .measurement_time(MEASUREMENT_TIME) + // Make the plots logarithmic, matching `SIZES`' scale. + .plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); + + group +} fn list_apply( group: &mut BenchmarkGroup, @@ -53,9 +72,7 @@ fn list_apply( } fn concrete_list_apply(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("concrete_list_apply"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("concrete_list_apply")); let empty_base = |_: usize| Vec::::new; let full_base = |size: usize| move || iter::repeat(0).take(size).collect::>(); @@ -77,9 +94,7 @@ fn concrete_list_apply(criterion: &mut Criterion) { } fn concrete_list_clone_dynamic(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("concrete_list_clone_dynamic"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("concrete_list_clone_dynamic")); for size in SIZES { group.throughput(Throughput::Elements(size as u64)); @@ -99,9 +114,7 @@ fn concrete_list_clone_dynamic(criterion: &mut Criterion) { } fn dynamic_list_push(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("dynamic_list_push"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("dynamic_list_push")); for size in SIZES { group.throughput(Throughput::Elements(size as u64)); @@ -130,9 +143,7 @@ fn dynamic_list_push(criterion: &mut Criterion) { } fn dynamic_list_apply(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("dynamic_list_apply"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("dynamic_list_apply")); let empty_base = |_: usize| || Vec::::new().clone_dynamic(); let full_base = |size: usize| move || iter::repeat(0).take(size).collect::>(); diff --git a/benches/benches/bevy_reflect/map.rs b/benches/benches/bevy_reflect/map.rs index fc9da0aa08..f8f47feccb 100644 --- a/benches/benches/bevy_reflect/map.rs +++ b/benches/benches/bevy_reflect/map.rs @@ -1,10 +1,11 @@ use core::{fmt::Write, iter, time::Duration}; +use benches::bench; use bevy_reflect::{DynamicMap, Map}; use bevy_utils::HashMap; use criterion::{ - black_box, criterion_group, measurement::Measurement, BatchSize, BenchmarkGroup, BenchmarkId, - Criterion, Throughput, + black_box, criterion_group, measurement::Measurement, AxisScale, BatchSize, BenchmarkGroup, + BenchmarkId, Criterion, PlotConfiguration, Throughput, }; criterion_group!( @@ -15,10 +16,30 @@ criterion_group!( dynamic_map_insert ); +// Use a shorter warm-up time (from 3 to 0.5 seconds) and measurement time (from 5 to 4) because we +// have so many combinations (>50) to benchmark. const WARM_UP_TIME: Duration = Duration::from_millis(500); const MEASUREMENT_TIME: Duration = Duration::from_secs(4); + +/// An array of list sizes used in benchmarks. +/// +/// This scales logarithmically. const SIZES: [usize; 5] = [100, 316, 1000, 3162, 10000]; +/// Creates a [`BenchmarkGroup`] with common configuration shared by all benchmarks within this +/// module. +fn create_group<'a, M: Measurement>(c: &'a mut Criterion, name: &str) -> BenchmarkGroup<'a, M> { + let mut group = c.benchmark_group(name); + + group + .warm_up_time(WARM_UP_TIME) + .measurement_time(MEASUREMENT_TIME) + // Make the plots logarithmic, matching `SIZES`' scale. + .plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); + + group +} + /// Generic benchmark for applying one `Map` to another. /// /// `f_base` is a function which takes an input size and produces a generator @@ -55,9 +76,7 @@ fn map_apply( } fn concrete_map_apply(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("concrete_map_apply"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("concrete_map_apply")); let empty_base = |_: usize| HashMap::::default; @@ -131,9 +150,7 @@ fn u64_to_n_byte_key(k: u64, n: usize) -> String { } fn dynamic_map_apply(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("dynamic_map_apply"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("dynamic_map_apply")); let empty_base = |_: usize| DynamicMap::default; @@ -199,9 +216,7 @@ fn dynamic_map_apply(criterion: &mut Criterion) { } fn dynamic_map_get(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("dynamic_map_get"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("dynamic_map_get")); for size in SIZES { group.throughput(Throughput::Elements(size as u64)); @@ -250,9 +265,7 @@ fn dynamic_map_get(criterion: &mut Criterion) { } fn dynamic_map_insert(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("dynamic_map_insert"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("dynamic_map_insert")); for size in SIZES { group.throughput(Throughput::Elements(size as u64)); diff --git a/benches/benches/bevy_reflect/path.rs b/benches/benches/bevy_reflect/path.rs index 2cca245239..9cf5d7250b 100644 --- a/benches/benches/bevy_reflect/path.rs +++ b/benches/benches/bevy_reflect/path.rs @@ -1,5 +1,6 @@ use core::{fmt::Write, str, time::Duration}; +use benches::bench; use bevy_reflect::ParsedPath; use criterion::{black_box, criterion_group, BatchSize, BenchmarkId, Criterion, Throughput}; use rand::{distributions::Uniform, Rng, SeedableRng}; @@ -11,7 +12,7 @@ const WARM_UP_TIME: Duration = Duration::from_millis(500); const MEASUREMENT_TIME: Duration = Duration::from_secs(2); const SAMPLE_SIZE: usize = 500; const NOISE_THRESHOLD: f64 = 0.03; -const SIZES: [usize; 6] = [100, 3160, 1000, 3_162, 10_000, 24_000]; +const SIZES: [usize; 6] = [100, 316, 1_000, 3_162, 10_000, 24_000]; fn deterministic_rand() -> ChaCha8Rng { ChaCha8Rng::seed_from_u64(42) @@ -66,23 +67,32 @@ fn mk_paths(size: usize) -> impl FnMut() -> String { } fn parse_reflect_path(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("parse_reflect_path"); + let mut group = criterion.benchmark_group(bench!("parse_reflect_path")); + group.warm_up_time(WARM_UP_TIME); group.measurement_time(MEASUREMENT_TIME); group.sample_size(SAMPLE_SIZE); group.noise_threshold(NOISE_THRESHOLD); - let group = &mut group; for size in SIZES { group.throughput(Throughput::Elements(size as u64)); + group.bench_with_input( - BenchmarkId::new("parse_reflect_path", size), + BenchmarkId::from_parameter(size), &size, |bencher, &size| { let mk_paths = mk_paths(size); bencher.iter_batched( mk_paths, - |path| assert!(ParsedPath::parse(black_box(&path)).is_ok()), + |path| { + let parsed_path = black_box(ParsedPath::parse(black_box(&path))); + + // When `cargo test --benches` is run, each benchmark is run once. This + // verifies that we are benchmarking a successful parse without it + // affecting the recorded time. + #[cfg(test)] + assert!(parsed_path.is_ok()); + }, BatchSize::SmallInput, ); }, diff --git a/benches/benches/bevy_reflect/struct.rs b/benches/benches/bevy_reflect/struct.rs index dfd324e705..d71d006536 100644 --- a/benches/benches/bevy_reflect/struct.rs +++ b/benches/benches/bevy_reflect/struct.rs @@ -1,7 +1,11 @@ use core::time::Duration; +use benches::bench; use bevy_reflect::{DynamicStruct, GetField, PartialReflect, Reflect, Struct}; -use criterion::{black_box, criterion_group, BatchSize, BenchmarkId, Criterion, Throughput}; +use criterion::{ + black_box, criterion_group, measurement::Measurement, AxisScale, BatchSize, BenchmarkGroup, + BenchmarkId, Criterion, PlotConfiguration, Throughput, +}; criterion_group!( benches, @@ -19,10 +23,22 @@ const WARM_UP_TIME: Duration = Duration::from_millis(500); const MEASUREMENT_TIME: Duration = Duration::from_secs(4); const SIZES: [usize; 4] = [16, 32, 64, 128]; +/// Creates a [`BenchmarkGroup`] with common configuration shared by all benchmarks within this +/// module. +fn create_group<'a, M: Measurement>(c: &'a mut Criterion, name: &str) -> BenchmarkGroup<'a, M> { + let mut group = c.benchmark_group(name); + + group + .warm_up_time(WARM_UP_TIME) + .measurement_time(MEASUREMENT_TIME) + // Make the plots logarithmic, matching `SIZES`' scale. + .plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); + + group +} + fn concrete_struct_field(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("concrete_struct_field"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("concrete_struct_field")); let structs: [Box; 4] = [ Box::new(Struct16::default()), @@ -44,7 +60,7 @@ fn concrete_struct_field(criterion: &mut Criterion) { bencher.iter(|| { for name in &field_names { - s.field(black_box(name)); + black_box(s.field(black_box(name))); } }); }, @@ -53,9 +69,7 @@ fn concrete_struct_field(criterion: &mut Criterion) { } fn concrete_struct_apply(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("concrete_struct_apply"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("concrete_struct_apply")); // Use functions that produce trait objects of varying concrete types as the // input to the benchmark. @@ -111,9 +125,7 @@ fn concrete_struct_apply(criterion: &mut Criterion) { } fn concrete_struct_type_info(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("concrete_struct_type_info"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("concrete_struct_type_info")); let structs: [(Box, Box); 5] = [ ( @@ -145,23 +157,21 @@ fn concrete_struct_type_info(criterion: &mut Criterion) { BenchmarkId::new("NonGeneric", field_count), &standard, |bencher, s| { - bencher.iter(|| black_box(s.get_represented_type_info())); + bencher.iter(|| s.get_represented_type_info()); }, ); group.bench_with_input( BenchmarkId::new("Generic", field_count), &generic, |bencher, s| { - bencher.iter(|| black_box(s.get_represented_type_info())); + bencher.iter(|| s.get_represented_type_info()); }, ); } } fn concrete_struct_clone(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("concrete_struct_clone"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("concrete_struct_clone")); let structs: [(Box, Box); 5] = [ ( @@ -193,23 +203,21 @@ fn concrete_struct_clone(criterion: &mut Criterion) { BenchmarkId::new("NonGeneric", field_count), &standard, |bencher, s| { - bencher.iter(|| black_box(s.clone_dynamic())); + bencher.iter(|| s.clone_dynamic()); }, ); group.bench_with_input( BenchmarkId::new("Generic", field_count), &generic, |bencher, s| { - bencher.iter(|| black_box(s.clone_dynamic())); + bencher.iter(|| s.clone_dynamic()); }, ); } } fn dynamic_struct_clone(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("dynamic_struct_clone"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("dynamic_struct_clone")); let structs: [Box; 5] = [ Box::new(Struct1::default().clone_dynamic()), @@ -226,16 +234,14 @@ fn dynamic_struct_clone(criterion: &mut Criterion) { BenchmarkId::from_parameter(field_count), &s, |bencher, s| { - bencher.iter(|| black_box(s.clone_dynamic())); + bencher.iter(|| s.clone_dynamic()); }, ); } } fn dynamic_struct_apply(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("dynamic_struct_apply"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("dynamic_struct_apply")); let patches: &[(fn() -> Box, usize)] = &[ (|| Box::new(Struct16::default()), 16), @@ -293,9 +299,7 @@ fn dynamic_struct_apply(criterion: &mut Criterion) { } fn dynamic_struct_insert(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("dynamic_struct_insert"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("dynamic_struct_insert")); for field_count in SIZES { group.throughput(Throughput::Elements(field_count as u64)); @@ -325,9 +329,7 @@ fn dynamic_struct_insert(criterion: &mut Criterion) { } fn dynamic_struct_get_field(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("dynamic_struct_get"); - group.warm_up_time(WARM_UP_TIME); - group.measurement_time(MEASUREMENT_TIME); + let mut group = create_group(criterion, bench!("dynamic_struct_get_field")); for field_count in SIZES { group.throughput(Throughput::Elements(field_count as u64)); @@ -342,9 +344,7 @@ fn dynamic_struct_get_field(criterion: &mut Criterion) { } let field = black_box("field_63"); - bencher.iter(|| { - black_box(s.get_field::<()>(field)); - }); + bencher.iter(|| s.get_field::<()>(field)); }, ); } diff --git a/benches/src/lib.rs b/benches/src/lib.rs new file mode 100644 index 0000000000..699ab13e86 --- /dev/null +++ b/benches/src/lib.rs @@ -0,0 +1,44 @@ +/// Automatically generates the qualified name of a benchmark given its function name and module +/// path. +/// +/// This macro takes a single string literal as input and returns a [`&'static str`](str). Its +/// result is determined at compile-time. If you need to create variations of a benchmark name +/// based on its input, use this in combination with [`BenchmarkId`](criterion::BenchmarkId). +/// +/// # When to use this +/// +/// Use this macro to name benchmarks that are not within a group and benchmark groups themselves. +/// You'll most commonly use this macro with: +/// +/// - [`Criterion::bench_function()`](criterion::Criterion::bench_function) +/// - [`Criterion::bench_with_input()`](criterion::Criterion::bench_with_input) +/// - [`Criterion::benchmark_group()`](criterion::Criterion::benchmark_group) +/// +/// You do not want to use this macro with +/// [`BenchmarkGroup::bench_function()`](criterion::BenchmarkGroup::bench_function) or +/// [`BenchmarkGroup::bench_with_input()`](criterion::BenchmarkGroup::bench_with_input), because +/// the group they are in already has the qualified path in it. +/// +/// # Example +/// +/// ``` +/// mod ecs { +/// mod query { +/// use criterion::Criterion; +/// use benches::bench; +/// +/// fn iter(c: &mut Criterion) { +/// // Benchmark name ends in `ecs::query::iter`. +/// c.bench_function(bench!("iter"), |b| { +/// // ... +/// }); +/// } +/// } +/// } +/// ``` +#[macro_export] +macro_rules! bench { + ($name:literal) => { + concat!(module_path!(), "::", $name) + }; +}