Migrate reflection benchmarks to new naming system (#16986)

# Objective

- Please see #16647 for the full reasoning behind this change.

## Solution

- Create the `bench!` macro, which generates the name of the benchmark
at compile time.

Migrating is a single line change, and it will automatically update if
you move the benchmark to a different module:

  ```diff
  + use benches::bench;

  fn my_benchmark(c: &mut Criterion) {
  -   c.bench_function("my_benchmark", |b| {});
  +   c.bench_function(bench!("my_benchmark"), |b| {});
  }
  ```

- Migrate all reflection benchmarks to use `bench!`.
- Fix a few places where `black_box()` or Criterion is misused.

## Testing

```sh
cd benches

# Will take a long time!
cargo bench --bench reflect

# List out the names of all reflection benchmarks, to ensure I didn't miss anything.
cargo bench --bench reflect -- --list

# Check for linter warnings.
cargo clippy --bench reflect

# Run each benchmark once.
cargo test --bench reflect
```
This commit is contained in:
BD103 2024-12-26 17:28:09 -05:00 committed by GitHub
parent 3eae8590cc
commit c03e494a26
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 263 additions and 166 deletions

View File

@ -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"

View File

@ -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<dyn Fn(i32, i32) -> 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<T: std::ops::Add<Output = T>>(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>(
_: T0,
_: T1,
_: T2,
_: T3,
_: T4,
_: T5,
_: T6,
_: T7,
_: T8,
_: T9,
) {
}
fn simple<T: std::ops::Add<Output = T>>(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>(
_: 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::<i8>.into_function(),
|func| func.with_overload(add::<i16>),
|| simple::<i8>.into_function(),
|func| func.with_overload(simple::<i16>),
BatchSize::SmallInput,
);
})
.bench_function("01_complex_overload", |b| {
.bench_function(BenchmarkId::new("complex_overload", 1), |b| {
b.iter_batched(
|| complex::<i8, i16, i32, i64, i128, u8, u16, u32, u64, u128>.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::<i8>.into_function(),
|| simple::<i8>.into_function(),
|func| {
func.with_overload(add::<i16>)
.with_overload(add::<i32>)
.with_overload(add::<i64>)
func.with_overload(simple::<i16>)
.with_overload(simple::<i32>)
.with_overload(simple::<i64>)
},
BatchSize::SmallInput,
);
})
.bench_function("03_complex_overload", |b| {
.bench_function(BenchmarkId::new("complex_overload", 3), |b| {
b.iter_batched(
|| complex::<i8, i16, i32, i64, i128, u8, u16, u32, u64, u128>.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::<i8>.into_function(),
|| simple::<i8>.into_function(),
|func| {
func.with_overload(add::<i16>)
.with_overload(add::<i32>)
.with_overload(add::<i64>)
.with_overload(add::<i128>)
.with_overload(add::<u8>)
.with_overload(add::<u16>)
.with_overload(add::<u32>)
.with_overload(add::<u64>)
.with_overload(add::<u128>)
func.with_overload(simple::<i16>)
.with_overload(simple::<i32>)
.with_overload(simple::<i64>)
.with_overload(simple::<i128>)
.with_overload(simple::<u8>)
.with_overload(simple::<u16>)
.with_overload(simple::<u32>)
.with_overload(simple::<u64>)
.with_overload(simple::<u128>)
},
BatchSize::SmallInput,
);
})
.bench_function("10_complex_overload", |b| {
.bench_function(BenchmarkId::new("complex_overload", 10), |b| {
b.iter_batched(
|| complex::<i8, i16, i32, i64, i128, u8, u16, u32, u64, u128>.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::<i8>.into_function(),
|func| func.with_overload(add::<i16>),
|| simple::<i8>.into_function(),
|func| func.with_overload(simple::<i16>),
BatchSize::SmallInput,
);
})
.bench_function("03_nested_simple_overload", |b| {
.bench_function(BenchmarkId::new("nested_simple_overload", 3), |b| {
b.iter_batched(
|| add::<i8>.into_function(),
|| simple::<i8>.into_function(),
|func| {
func.with_overload(
add::<i16>
.into_function()
.with_overload(add::<i32>.into_function().with_overload(add::<i64>)),
simple::<i16>.into_function().with_overload(
simple::<i32>.into_function().with_overload(simple::<i64>),
),
)
},
BatchSize::SmallInput,
);
})
.bench_function("10_nested_simple_overload", |b| {
.bench_function(BenchmarkId::new("nested_simple_overload", 10), |b| {
b.iter_batched(
|| add::<i8>.into_function(),
|| simple::<i8>.into_function(),
|func| {
func.with_overload(
add::<i16>.into_function().with_overload(
add::<i32>.into_function().with_overload(
add::<i64>.into_function().with_overload(
add::<i128>.into_function().with_overload(
add::<u8>.into_function().with_overload(
add::<u16>.into_function().with_overload(
add::<u32>.into_function().with_overload(
add::<u64>
simple::<i16>.into_function().with_overload(
simple::<i32>.into_function().with_overload(
simple::<i64>.into_function().with_overload(
simple::<i128>.into_function().with_overload(
simple::<u8>.into_function().with_overload(
simple::<u16>.into_function().with_overload(
simple::<u32>.into_function().with_overload(
simple::<u64>
.into_function()
.with_overload(add::<u128>),
.with_overload(simple::<u128>),
),
),
),
@ -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::<i8>.into_function().with_overload(add::<i16>),
simple::<i8>.into_function().with_overload(simple::<i16>),
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::<i8>
simple::<i8>
.into_function()
.with_overload(add::<i16>)
.with_overload(add::<i32>)
.with_overload(add::<i64>),
.with_overload(simple::<i16>)
.with_overload(simple::<i32>)
.with_overload(simple::<i64>),
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::<i8>
simple::<i8>
.into_function()
.with_overload(add::<i16>)
.with_overload(add::<i32>)
.with_overload(add::<i64>)
.with_overload(add::<i128>)
.with_overload(add::<u8>)
.with_overload(add::<u16>)
.with_overload(add::<u32>)
.with_overload(add::<u64>)
.with_overload(add::<u128>),
.with_overload(simple::<i16>)
.with_overload(simple::<i32>)
.with_overload(simple::<i64>)
.with_overload(simple::<i128>)
.with_overload(simple::<u8>)
.with_overload(simple::<u16>)
.with_overload(simple::<u32>)
.with_overload(simple::<u64>)
.with_overload(simple::<u128>),
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());
});
}

View File

@ -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<M>, 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<M, LBase, LPatch, F1, F2, F3>(
group: &mut BenchmarkGroup<M>,
@ -53,9 +72,7 @@ fn list_apply<M, LBase, LPatch, F1, F2, F3>(
}
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::<u64>::new;
let full_base = |size: usize| move || iter::repeat(0).take(size).collect::<Vec<u64>>();
@ -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::<u64>::new().clone_dynamic();
let full_base = |size: usize| move || iter::repeat(0).take(size).collect::<Vec<u64>>();

View File

@ -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<M>, 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<M, MBase, MPatch, F1, F2, F3>(
}
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::<u64, u64>::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));

View File

@ -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,
);
},

View File

@ -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<M>, 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<dyn Struct>; 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<dyn Struct>, Box<dyn Struct>); 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<dyn Struct>, Box<dyn Struct>); 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<dyn Struct>; 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<dyn PartialReflect>, 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));
},
);
}

44
benches/src/lib.rs Normal file
View File

@ -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)
};
}