bench: add bevy_reflect::{List, Map, Struct}
benchmarks (#3690)
# Objective Partially addresses #3594. ## Solution This adds basic benchmarks for `List`, `Map`, and `Struct` implementors, both concrete (`Vec`, `HashMap`, and defined struct types) and dynamic (`DynamicList`, `DynamicMap` and `DynamicStruct`). A few insights from the benchmarks (all measurements are local on my machine): - Applying a list with many elements to a list with no elements is slower than applying to a list of the same length: - 3-4x slower when applying to a `Vec` - 5-6x slower when applying to a `DynamicList` I suspect this could be improved by `reserve()`ing the correct length up front, but haven't tested. - Applying a `DynamicMap` to another `Map` is linear in the number of elements, but applying a `HashMap` seems to be at least quadratic. No intuition on this one. - Applying like structs (concrete -> concrete, `DynamicStruct` -> `DynamicStruct`) seems to be faster than applying unlike structs.
This commit is contained in:
parent
1648c89b64
commit
0917c49b9b
@ -12,7 +12,9 @@ rand = "0.8"
|
|||||||
rand_chacha = "0.3"
|
rand_chacha = "0.3"
|
||||||
criterion = { version = "0.3", features = ["html_reports"] }
|
criterion = { version = "0.3", features = ["html_reports"] }
|
||||||
bevy_ecs = { path = "../crates/bevy_ecs" }
|
bevy_ecs = { path = "../crates/bevy_ecs" }
|
||||||
|
bevy_reflect = { path = "../crates/bevy_reflect" }
|
||||||
bevy_tasks = { path = "../crates/bevy_tasks" }
|
bevy_tasks = { path = "../crates/bevy_tasks" }
|
||||||
|
bevy_utils = { path = "../crates/bevy_utils" }
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "archetype_updates"
|
name = "archetype_updates"
|
||||||
@ -24,11 +26,6 @@ name = "ecs_bench_suite"
|
|||||||
path = "benches/bevy_ecs/ecs_bench_suite/mod.rs"
|
path = "benches/bevy_ecs/ecs_bench_suite/mod.rs"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "system_stage"
|
|
||||||
path = "benches/bevy_ecs/stages.rs"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "run_criteria"
|
name = "run_criteria"
|
||||||
path = "benches/bevy_ecs/run_criteria.rs"
|
path = "benches/bevy_ecs/run_criteria.rs"
|
||||||
@ -39,11 +36,31 @@ name = "commands"
|
|||||||
path = "benches/bevy_ecs/commands.rs"
|
path = "benches/bevy_ecs/commands.rs"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "system_stage"
|
||||||
|
path = "benches/bevy_ecs/stages.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "world_get"
|
name = "world_get"
|
||||||
path = "benches/bevy_ecs/world_get.rs"
|
path = "benches/bevy_ecs/world_get.rs"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "reflect_list"
|
||||||
|
path = "benches/bevy_reflect/list.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "reflect_map"
|
||||||
|
path = "benches/bevy_reflect/map.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "reflect_struct"
|
||||||
|
path = "benches/bevy_reflect/struct.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "iter"
|
name = "iter"
|
||||||
path = "benches/bevy_tasks/iter.rs"
|
path = "benches/bevy_tasks/iter.rs"
|
||||||
|
155
benches/benches/bevy_reflect/list.rs
Normal file
155
benches/benches/bevy_reflect/list.rs
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
use std::{iter, time::Duration};
|
||||||
|
|
||||||
|
use bevy_reflect::{DynamicList, List};
|
||||||
|
use criterion::{
|
||||||
|
black_box, criterion_group, criterion_main, measurement::Measurement, BatchSize,
|
||||||
|
BenchmarkGroup, BenchmarkId, Criterion, Throughput,
|
||||||
|
};
|
||||||
|
|
||||||
|
criterion_group!(
|
||||||
|
benches,
|
||||||
|
concrete_list_apply,
|
||||||
|
concrete_list_clone_dynamic,
|
||||||
|
dynamic_list_apply,
|
||||||
|
dynamic_list_push
|
||||||
|
);
|
||||||
|
criterion_main!(benches);
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
fn list_apply<M, LBase, LPatch, F1, F2, F3>(
|
||||||
|
group: &mut BenchmarkGroup<M>,
|
||||||
|
bench_name: &str,
|
||||||
|
f_base: F1,
|
||||||
|
f_patch: F3,
|
||||||
|
) where
|
||||||
|
M: Measurement,
|
||||||
|
LBase: List,
|
||||||
|
LPatch: List,
|
||||||
|
F1: Fn(usize) -> F2,
|
||||||
|
F2: Fn() -> LBase,
|
||||||
|
F3: Fn(usize) -> LPatch,
|
||||||
|
{
|
||||||
|
for size in SIZES {
|
||||||
|
group.throughput(Throughput::Elements(size as u64));
|
||||||
|
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::new(bench_name, size),
|
||||||
|
&size,
|
||||||
|
|bencher, &size| {
|
||||||
|
let f_base = f_base(size);
|
||||||
|
let patch = f_patch(size);
|
||||||
|
bencher.iter_batched(
|
||||||
|
|| f_base(),
|
||||||
|
|mut base| base.apply(black_box(&patch)),
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 empty_base = |_: usize| || Vec::<u64>::new();
|
||||||
|
let full_base = |size: usize| move || iter::repeat(0).take(size).collect::<Vec<u64>>();
|
||||||
|
let patch = |size: usize| iter::repeat(1).take(size).collect::<Vec<u64>>();
|
||||||
|
|
||||||
|
list_apply(&mut group, "empty_base_concrete_patch", empty_base, patch);
|
||||||
|
|
||||||
|
list_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| {
|
||||||
|
patch(size).clone_dynamic()
|
||||||
|
});
|
||||||
|
|
||||||
|
list_apply(&mut group, "same_len_concrete_patch", full_base, patch);
|
||||||
|
|
||||||
|
list_apply(&mut group, "same_len_dynamic_patch", full_base, |size| {
|
||||||
|
patch(size).clone_dynamic()
|
||||||
|
});
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
for size in SIZES {
|
||||||
|
group.throughput(Throughput::Elements(size as u64));
|
||||||
|
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::from_parameter(size),
|
||||||
|
&size,
|
||||||
|
|bencher, &size| {
|
||||||
|
let v = iter::repeat(0).take(size).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
bencher.iter(|| black_box(&v).clone_dynamic());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
for size in SIZES {
|
||||||
|
group.throughput(Throughput::Elements(size as u64));
|
||||||
|
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::from_parameter(size),
|
||||||
|
&size,
|
||||||
|
|bencher, &size| {
|
||||||
|
let src = iter::repeat(()).take(size).collect::<Vec<_>>();
|
||||||
|
let dst = DynamicList::default();
|
||||||
|
|
||||||
|
bencher.iter_batched(
|
||||||
|
|| (src.clone(), dst.clone_dynamic()),
|
||||||
|
|(src, mut dst)| {
|
||||||
|
for item in src {
|
||||||
|
dst.push(item);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 empty_base = |_: usize| || Vec::<u64>::new().clone_dynamic();
|
||||||
|
let full_base = |size: usize| move || iter::repeat(0).take(size).collect::<Vec<u64>>();
|
||||||
|
let patch = |size: usize| iter::repeat(1).take(size).collect::<Vec<u64>>();
|
||||||
|
|
||||||
|
list_apply(&mut group, "empty_base_concrete_patch", empty_base, patch);
|
||||||
|
|
||||||
|
list_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| {
|
||||||
|
patch(size).clone_dynamic()
|
||||||
|
});
|
||||||
|
|
||||||
|
list_apply(&mut group, "same_len_concrete_patch", full_base, patch);
|
||||||
|
|
||||||
|
list_apply(&mut group, "same_len_dynamic_patch", full_base, |size| {
|
||||||
|
patch(size).clone_dynamic()
|
||||||
|
});
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
303
benches/benches/bevy_reflect/map.rs
Normal file
303
benches/benches/bevy_reflect/map.rs
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
use std::{fmt::Write, iter, time::Duration};
|
||||||
|
|
||||||
|
use bevy_reflect::{DynamicMap, Map};
|
||||||
|
use bevy_utils::HashMap;
|
||||||
|
use criterion::{
|
||||||
|
black_box, criterion_group, criterion_main, measurement::Measurement, BatchSize,
|
||||||
|
BenchmarkGroup, BenchmarkId, Criterion, Throughput,
|
||||||
|
};
|
||||||
|
|
||||||
|
criterion_group!(
|
||||||
|
benches,
|
||||||
|
concrete_map_apply,
|
||||||
|
dynamic_map_apply,
|
||||||
|
dynamic_map_get,
|
||||||
|
dynamic_map_insert
|
||||||
|
);
|
||||||
|
criterion_main!(benches);
|
||||||
|
|
||||||
|
const WARM_UP_TIME: Duration = Duration::from_millis(500);
|
||||||
|
const MEASUREMENT_TIME: Duration = Duration::from_secs(4);
|
||||||
|
const SIZES: [usize; 5] = [100, 316, 1000, 3162, 10000];
|
||||||
|
|
||||||
|
/// Generic benchmark for applying one `Map` to another.
|
||||||
|
///
|
||||||
|
/// `f_base` is a function which takes an input size and produces a generator
|
||||||
|
/// for new base maps. `f_patch` is a function which produces a map to be
|
||||||
|
/// applied to the base map.
|
||||||
|
fn map_apply<M, MBase, MPatch, F1, F2, F3>(
|
||||||
|
group: &mut BenchmarkGroup<M>,
|
||||||
|
bench_name: &str,
|
||||||
|
f_base: F1,
|
||||||
|
f_patch: F3,
|
||||||
|
) where
|
||||||
|
M: Measurement,
|
||||||
|
MBase: Map,
|
||||||
|
MPatch: Map,
|
||||||
|
F1: Fn(usize) -> F2,
|
||||||
|
F2: Fn() -> MBase,
|
||||||
|
F3: Fn(usize) -> MPatch,
|
||||||
|
{
|
||||||
|
for size in SIZES {
|
||||||
|
group.throughput(Throughput::Elements(size as u64));
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::new(bench_name, size),
|
||||||
|
&size,
|
||||||
|
|bencher, &size| {
|
||||||
|
let f_base = f_base(size);
|
||||||
|
bencher.iter_batched(
|
||||||
|
|| (f_base(), f_patch(size)),
|
||||||
|
|(mut base, patch)| base.apply(black_box(&patch)),
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 empty_base = |_: usize| || HashMap::<u64, u64>::default();
|
||||||
|
|
||||||
|
let key_range_base = |size: usize| {
|
||||||
|
move || {
|
||||||
|
(0..size as u64)
|
||||||
|
.zip(iter::repeat(0))
|
||||||
|
.collect::<HashMap<u64, u64>>()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let key_range_patch = |size: usize| {
|
||||||
|
(0..size as u64)
|
||||||
|
.zip(iter::repeat(1))
|
||||||
|
.collect::<HashMap<u64, u64>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
let disjoint_patch = |size: usize| {
|
||||||
|
(size as u64..2 * size as u64)
|
||||||
|
.zip(iter::repeat(1))
|
||||||
|
.collect::<HashMap<u64, u64>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
map_apply(
|
||||||
|
&mut group,
|
||||||
|
"empty_base_concrete_patch",
|
||||||
|
empty_base,
|
||||||
|
key_range_patch,
|
||||||
|
);
|
||||||
|
|
||||||
|
map_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| {
|
||||||
|
key_range_patch(size).clone_dynamic()
|
||||||
|
});
|
||||||
|
|
||||||
|
map_apply(
|
||||||
|
&mut group,
|
||||||
|
"same_keys_concrete_patch",
|
||||||
|
key_range_base,
|
||||||
|
key_range_patch,
|
||||||
|
);
|
||||||
|
|
||||||
|
map_apply(
|
||||||
|
&mut group,
|
||||||
|
"same_keys_dynamic_patch",
|
||||||
|
key_range_base,
|
||||||
|
|size| key_range_patch(size).clone_dynamic(),
|
||||||
|
);
|
||||||
|
|
||||||
|
map_apply(
|
||||||
|
&mut group,
|
||||||
|
"disjoint_keys_concrete_patch",
|
||||||
|
key_range_base,
|
||||||
|
disjoint_patch,
|
||||||
|
);
|
||||||
|
|
||||||
|
map_apply(
|
||||||
|
&mut group,
|
||||||
|
"disjoint_keys_dynamic_patch",
|
||||||
|
key_range_base,
|
||||||
|
|size| disjoint_patch(size).clone_dynamic(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn u64_to_n_byte_key(k: u64, n: usize) -> String {
|
||||||
|
let mut key = String::with_capacity(n);
|
||||||
|
write!(&mut key, "{}", k).unwrap();
|
||||||
|
|
||||||
|
// Pad key to n bytes.
|
||||||
|
key.extend(iter::repeat('\0').take(n - key.len()));
|
||||||
|
key
|
||||||
|
}
|
||||||
|
|
||||||
|
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 empty_base = |_: usize| || DynamicMap::default();
|
||||||
|
|
||||||
|
let key_range_base = |size: usize| {
|
||||||
|
move || {
|
||||||
|
(0..size as u64)
|
||||||
|
.zip(iter::repeat(0))
|
||||||
|
.collect::<HashMap<u64, u64>>()
|
||||||
|
.clone_dynamic()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let key_range_patch = |size: usize| {
|
||||||
|
(0..size as u64)
|
||||||
|
.zip(iter::repeat(1))
|
||||||
|
.collect::<HashMap<u64, u64>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
let disjoint_patch = |size: usize| {
|
||||||
|
(size as u64..2 * size as u64)
|
||||||
|
.zip(iter::repeat(1))
|
||||||
|
.collect::<HashMap<u64, u64>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
map_apply(
|
||||||
|
&mut group,
|
||||||
|
"empty_base_concrete_patch",
|
||||||
|
empty_base,
|
||||||
|
key_range_patch,
|
||||||
|
);
|
||||||
|
|
||||||
|
map_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| {
|
||||||
|
key_range_patch(size).clone_dynamic()
|
||||||
|
});
|
||||||
|
|
||||||
|
map_apply(
|
||||||
|
&mut group,
|
||||||
|
"same_keys_concrete_patch",
|
||||||
|
key_range_base,
|
||||||
|
key_range_patch,
|
||||||
|
);
|
||||||
|
|
||||||
|
map_apply(
|
||||||
|
&mut group,
|
||||||
|
"same_keys_dynamic_patch",
|
||||||
|
key_range_base,
|
||||||
|
|size| key_range_patch(size).clone_dynamic(),
|
||||||
|
);
|
||||||
|
|
||||||
|
map_apply(
|
||||||
|
&mut group,
|
||||||
|
"disjoint_keys_concrete_patch",
|
||||||
|
key_range_base,
|
||||||
|
disjoint_patch,
|
||||||
|
);
|
||||||
|
|
||||||
|
map_apply(
|
||||||
|
&mut group,
|
||||||
|
"disjoint_keys_dynamic_patch",
|
||||||
|
key_range_base,
|
||||||
|
|size| disjoint_patch(size).clone_dynamic(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
for size in SIZES {
|
||||||
|
group.throughput(Throughput::Elements(size as u64));
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::new("u64_keys", size),
|
||||||
|
&size,
|
||||||
|
|bencher, &size| {
|
||||||
|
let mut map = DynamicMap::default();
|
||||||
|
for i in 0..size as u64 {
|
||||||
|
map.insert(i, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
for i in 0..size as u64 {
|
||||||
|
let key = black_box(i);
|
||||||
|
black_box(assert!(map.get(&key).is_some()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for size in SIZES {
|
||||||
|
group.throughput(Throughput::Elements(size as u64));
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::new("64_byte_keys", size),
|
||||||
|
&size,
|
||||||
|
|bencher, &size| {
|
||||||
|
let mut map = DynamicMap::default();
|
||||||
|
let mut keys = Vec::with_capacity(size);
|
||||||
|
for i in 0..size as u64 {
|
||||||
|
let key = u64_to_n_byte_key(i, 64);
|
||||||
|
map.insert(key.clone(), i);
|
||||||
|
keys.push(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
for i in 0..size {
|
||||||
|
let key = black_box(&keys[i]);
|
||||||
|
assert!(map.get(key).is_some());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
for size in SIZES {
|
||||||
|
group.throughput(Throughput::Elements(size as u64));
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::new("u64_keys", size),
|
||||||
|
&size,
|
||||||
|
|bencher, &size| {
|
||||||
|
bencher.iter_batched(
|
||||||
|
|| DynamicMap::default(),
|
||||||
|
|mut map| {
|
||||||
|
for i in 0..size as u64 {
|
||||||
|
let key = black_box(i);
|
||||||
|
black_box(map.insert(key, i));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for size in SIZES {
|
||||||
|
group.throughput(Throughput::Elements(size as u64));
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::new("64_byte_keys", size),
|
||||||
|
&size,
|
||||||
|
|bencher, &size| {
|
||||||
|
let mut keys = Vec::with_capacity(size);
|
||||||
|
for i in 0..size {
|
||||||
|
let key = u64_to_n_byte_key(i as u64, 64);
|
||||||
|
keys.push(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bencher.iter_batched(
|
||||||
|
|| (DynamicMap::default(), keys.clone()),
|
||||||
|
|(mut map, keys)| {
|
||||||
|
for (i, key) in keys.into_iter().enumerate() {
|
||||||
|
let key = black_box(key);
|
||||||
|
map.insert(key, i);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
485
benches/benches/bevy_reflect/struct.rs
Normal file
485
benches/benches/bevy_reflect/struct.rs
Normal file
@ -0,0 +1,485 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use bevy_reflect::{DynamicStruct, GetField, Reflect, Struct};
|
||||||
|
use criterion::{
|
||||||
|
black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput,
|
||||||
|
};
|
||||||
|
|
||||||
|
criterion_group!(
|
||||||
|
benches,
|
||||||
|
concrete_struct_apply,
|
||||||
|
concrete_struct_field,
|
||||||
|
dynamic_struct_apply,
|
||||||
|
dynamic_struct_get_field,
|
||||||
|
dynamic_struct_insert,
|
||||||
|
);
|
||||||
|
criterion_main!(benches);
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
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 structs: [Box<dyn Struct>; 4] = [
|
||||||
|
Box::new(Struct16::default()),
|
||||||
|
Box::new(Struct32::default()),
|
||||||
|
Box::new(Struct64::default()),
|
||||||
|
Box::new(Struct128::default()),
|
||||||
|
];
|
||||||
|
|
||||||
|
for s in structs {
|
||||||
|
let field_count = s.field_len();
|
||||||
|
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::from_parameter(field_count),
|
||||||
|
&s,
|
||||||
|
|bencher, s| {
|
||||||
|
let field_names = (0..field_count)
|
||||||
|
.map(|i| format!("field_{}", i))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
for name in field_names.iter() {
|
||||||
|
s.field(black_box(name));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Use functions that produce trait objects of varying concrete types as the
|
||||||
|
// input to the benchmark.
|
||||||
|
let inputs: &[fn() -> (Box<dyn Struct>, Box<dyn Reflect>)] = &[
|
||||||
|
|| (Box::new(Struct16::default()), Box::new(Struct16::default())),
|
||||||
|
|| (Box::new(Struct32::default()), Box::new(Struct32::default())),
|
||||||
|
|| (Box::new(Struct64::default()), Box::new(Struct64::default())),
|
||||||
|
|| {
|
||||||
|
(
|
||||||
|
Box::new(Struct128::default()),
|
||||||
|
Box::new(Struct128::default()),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for input in inputs {
|
||||||
|
let field_count = input().0.field_len();
|
||||||
|
group.throughput(Throughput::Elements(field_count as u64));
|
||||||
|
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::new("apply_concrete", field_count),
|
||||||
|
input,
|
||||||
|
|bencher, input| {
|
||||||
|
bencher.iter_batched(
|
||||||
|
input,
|
||||||
|
|(mut obj, patch)| obj.apply(black_box(patch.as_ref())),
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for input in inputs {
|
||||||
|
let field_count = input().0.field_len();
|
||||||
|
group.throughput(Throughput::Elements(field_count as u64));
|
||||||
|
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::new("apply_dynamic", field_count),
|
||||||
|
input,
|
||||||
|
|bencher, input| {
|
||||||
|
bencher.iter_batched(
|
||||||
|
|| {
|
||||||
|
let (obj, _) = input();
|
||||||
|
let patch = obj.clone_dynamic();
|
||||||
|
(obj, patch)
|
||||||
|
},
|
||||||
|
|(mut obj, patch)| obj.apply(black_box(&patch)),
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 patches: &[(fn() -> Box<dyn Reflect>, usize)] = &[
|
||||||
|
(|| Box::new(Struct16::default()), 16),
|
||||||
|
(|| Box::new(Struct32::default()), 32),
|
||||||
|
(|| Box::new(Struct64::default()), 64),
|
||||||
|
(|| Box::new(Struct128::default()), 128),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (patch, field_count) in patches {
|
||||||
|
let field_count = *field_count;
|
||||||
|
group.throughput(Throughput::Elements(field_count as u64));
|
||||||
|
|
||||||
|
let mut base = DynamicStruct::default();
|
||||||
|
for i in 0..field_count {
|
||||||
|
let field_name = format!("field_{}", i);
|
||||||
|
base.insert(&field_name, 1u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::new("apply_concrete", field_count),
|
||||||
|
&patch,
|
||||||
|
|bencher, patch| {
|
||||||
|
bencher.iter_batched(
|
||||||
|
|| (base.clone_dynamic(), patch()),
|
||||||
|
|(mut base, patch)| base.apply(black_box(&*patch)),
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for field_count in SIZES {
|
||||||
|
group.throughput(Throughput::Elements(field_count as u64));
|
||||||
|
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::new("apply_dynamic", field_count),
|
||||||
|
&field_count,
|
||||||
|
|bencher, &field_count| {
|
||||||
|
let mut base = DynamicStruct::default();
|
||||||
|
let mut patch = DynamicStruct::default();
|
||||||
|
for i in 0..field_count {
|
||||||
|
let field_name = format!("field_{}", i);
|
||||||
|
base.insert(&field_name, 0u32);
|
||||||
|
patch.insert(&field_name, 1u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
bencher.iter_batched(
|
||||||
|
|| base.clone_dynamic(),
|
||||||
|
|mut base| base.apply(black_box(&patch)),
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
for field_count in SIZES {
|
||||||
|
group.throughput(Throughput::Elements(field_count as u64));
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::from_parameter(field_count),
|
||||||
|
&field_count,
|
||||||
|
|bencher, field_count| {
|
||||||
|
let mut s = DynamicStruct::default();
|
||||||
|
for i in 0..*field_count {
|
||||||
|
let field_name = format!("field_{}", i);
|
||||||
|
s.insert(&field_name, ());
|
||||||
|
}
|
||||||
|
|
||||||
|
let field = format!("field_{}", field_count);
|
||||||
|
bencher.iter_batched(
|
||||||
|
|| s.clone_dynamic(),
|
||||||
|
|mut s| {
|
||||||
|
black_box(s.insert(black_box(&field), ()));
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
for field_count in SIZES {
|
||||||
|
group.throughput(Throughput::Elements(field_count as u64));
|
||||||
|
group.bench_with_input(
|
||||||
|
BenchmarkId::from_parameter(field_count),
|
||||||
|
&field_count,
|
||||||
|
|bencher, field_count| {
|
||||||
|
let mut s = DynamicStruct::default();
|
||||||
|
for i in 0..*field_count {
|
||||||
|
let field_name = format!("field_{}", i);
|
||||||
|
s.insert(&field_name, ());
|
||||||
|
}
|
||||||
|
|
||||||
|
let field = black_box("field_63");
|
||||||
|
bencher.iter(|| {
|
||||||
|
black_box(s.get_field::<()>(field));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Reflect)]
|
||||||
|
struct Struct16 {
|
||||||
|
field_0: u32,
|
||||||
|
field_1: u32,
|
||||||
|
field_2: u32,
|
||||||
|
field_3: u32,
|
||||||
|
field_4: u32,
|
||||||
|
field_5: u32,
|
||||||
|
field_6: u32,
|
||||||
|
field_7: u32,
|
||||||
|
field_8: u32,
|
||||||
|
field_9: u32,
|
||||||
|
field_10: u32,
|
||||||
|
field_11: u32,
|
||||||
|
field_12: u32,
|
||||||
|
field_13: u32,
|
||||||
|
field_14: u32,
|
||||||
|
field_15: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Reflect)]
|
||||||
|
struct Struct32 {
|
||||||
|
field_0: u32,
|
||||||
|
field_1: u32,
|
||||||
|
field_2: u32,
|
||||||
|
field_3: u32,
|
||||||
|
field_4: u32,
|
||||||
|
field_5: u32,
|
||||||
|
field_6: u32,
|
||||||
|
field_7: u32,
|
||||||
|
field_8: u32,
|
||||||
|
field_9: u32,
|
||||||
|
field_10: u32,
|
||||||
|
field_11: u32,
|
||||||
|
field_12: u32,
|
||||||
|
field_13: u32,
|
||||||
|
field_14: u32,
|
||||||
|
field_15: u32,
|
||||||
|
field_16: u32,
|
||||||
|
field_17: u32,
|
||||||
|
field_18: u32,
|
||||||
|
field_19: u32,
|
||||||
|
field_20: u32,
|
||||||
|
field_21: u32,
|
||||||
|
field_22: u32,
|
||||||
|
field_23: u32,
|
||||||
|
field_24: u32,
|
||||||
|
field_25: u32,
|
||||||
|
field_26: u32,
|
||||||
|
field_27: u32,
|
||||||
|
field_28: u32,
|
||||||
|
field_29: u32,
|
||||||
|
field_30: u32,
|
||||||
|
field_31: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Reflect)]
|
||||||
|
struct Struct64 {
|
||||||
|
field_0: u32,
|
||||||
|
field_1: u32,
|
||||||
|
field_2: u32,
|
||||||
|
field_3: u32,
|
||||||
|
field_4: u32,
|
||||||
|
field_5: u32,
|
||||||
|
field_6: u32,
|
||||||
|
field_7: u32,
|
||||||
|
field_8: u32,
|
||||||
|
field_9: u32,
|
||||||
|
field_10: u32,
|
||||||
|
field_11: u32,
|
||||||
|
field_12: u32,
|
||||||
|
field_13: u32,
|
||||||
|
field_14: u32,
|
||||||
|
field_15: u32,
|
||||||
|
field_16: u32,
|
||||||
|
field_17: u32,
|
||||||
|
field_18: u32,
|
||||||
|
field_19: u32,
|
||||||
|
field_20: u32,
|
||||||
|
field_21: u32,
|
||||||
|
field_22: u32,
|
||||||
|
field_23: u32,
|
||||||
|
field_24: u32,
|
||||||
|
field_25: u32,
|
||||||
|
field_26: u32,
|
||||||
|
field_27: u32,
|
||||||
|
field_28: u32,
|
||||||
|
field_29: u32,
|
||||||
|
field_30: u32,
|
||||||
|
field_31: u32,
|
||||||
|
field_32: u32,
|
||||||
|
field_33: u32,
|
||||||
|
field_34: u32,
|
||||||
|
field_35: u32,
|
||||||
|
field_36: u32,
|
||||||
|
field_37: u32,
|
||||||
|
field_38: u32,
|
||||||
|
field_39: u32,
|
||||||
|
field_40: u32,
|
||||||
|
field_41: u32,
|
||||||
|
field_42: u32,
|
||||||
|
field_43: u32,
|
||||||
|
field_44: u32,
|
||||||
|
field_45: u32,
|
||||||
|
field_46: u32,
|
||||||
|
field_47: u32,
|
||||||
|
field_48: u32,
|
||||||
|
field_49: u32,
|
||||||
|
field_50: u32,
|
||||||
|
field_51: u32,
|
||||||
|
field_52: u32,
|
||||||
|
field_53: u32,
|
||||||
|
field_54: u32,
|
||||||
|
field_55: u32,
|
||||||
|
field_56: u32,
|
||||||
|
field_57: u32,
|
||||||
|
field_58: u32,
|
||||||
|
field_59: u32,
|
||||||
|
field_60: u32,
|
||||||
|
field_61: u32,
|
||||||
|
field_62: u32,
|
||||||
|
field_63: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Reflect)]
|
||||||
|
struct Struct128 {
|
||||||
|
field_0: u32,
|
||||||
|
field_1: u32,
|
||||||
|
field_2: u32,
|
||||||
|
field_3: u32,
|
||||||
|
field_4: u32,
|
||||||
|
field_5: u32,
|
||||||
|
field_6: u32,
|
||||||
|
field_7: u32,
|
||||||
|
field_8: u32,
|
||||||
|
field_9: u32,
|
||||||
|
field_10: u32,
|
||||||
|
field_11: u32,
|
||||||
|
field_12: u32,
|
||||||
|
field_13: u32,
|
||||||
|
field_14: u32,
|
||||||
|
field_15: u32,
|
||||||
|
field_16: u32,
|
||||||
|
field_17: u32,
|
||||||
|
field_18: u32,
|
||||||
|
field_19: u32,
|
||||||
|
field_20: u32,
|
||||||
|
field_21: u32,
|
||||||
|
field_22: u32,
|
||||||
|
field_23: u32,
|
||||||
|
field_24: u32,
|
||||||
|
field_25: u32,
|
||||||
|
field_26: u32,
|
||||||
|
field_27: u32,
|
||||||
|
field_28: u32,
|
||||||
|
field_29: u32,
|
||||||
|
field_30: u32,
|
||||||
|
field_31: u32,
|
||||||
|
field_32: u32,
|
||||||
|
field_33: u32,
|
||||||
|
field_34: u32,
|
||||||
|
field_35: u32,
|
||||||
|
field_36: u32,
|
||||||
|
field_37: u32,
|
||||||
|
field_38: u32,
|
||||||
|
field_39: u32,
|
||||||
|
field_40: u32,
|
||||||
|
field_41: u32,
|
||||||
|
field_42: u32,
|
||||||
|
field_43: u32,
|
||||||
|
field_44: u32,
|
||||||
|
field_45: u32,
|
||||||
|
field_46: u32,
|
||||||
|
field_47: u32,
|
||||||
|
field_48: u32,
|
||||||
|
field_49: u32,
|
||||||
|
field_50: u32,
|
||||||
|
field_51: u32,
|
||||||
|
field_52: u32,
|
||||||
|
field_53: u32,
|
||||||
|
field_54: u32,
|
||||||
|
field_55: u32,
|
||||||
|
field_56: u32,
|
||||||
|
field_57: u32,
|
||||||
|
field_58: u32,
|
||||||
|
field_59: u32,
|
||||||
|
field_60: u32,
|
||||||
|
field_61: u32,
|
||||||
|
field_62: u32,
|
||||||
|
field_63: u32,
|
||||||
|
field_64: u32,
|
||||||
|
field_65: u32,
|
||||||
|
field_66: u32,
|
||||||
|
field_67: u32,
|
||||||
|
field_68: u32,
|
||||||
|
field_69: u32,
|
||||||
|
field_70: u32,
|
||||||
|
field_71: u32,
|
||||||
|
field_72: u32,
|
||||||
|
field_73: u32,
|
||||||
|
field_74: u32,
|
||||||
|
field_75: u32,
|
||||||
|
field_76: u32,
|
||||||
|
field_77: u32,
|
||||||
|
field_78: u32,
|
||||||
|
field_79: u32,
|
||||||
|
field_80: u32,
|
||||||
|
field_81: u32,
|
||||||
|
field_82: u32,
|
||||||
|
field_83: u32,
|
||||||
|
field_84: u32,
|
||||||
|
field_85: u32,
|
||||||
|
field_86: u32,
|
||||||
|
field_87: u32,
|
||||||
|
field_88: u32,
|
||||||
|
field_89: u32,
|
||||||
|
field_90: u32,
|
||||||
|
field_91: u32,
|
||||||
|
field_92: u32,
|
||||||
|
field_93: u32,
|
||||||
|
field_94: u32,
|
||||||
|
field_95: u32,
|
||||||
|
field_96: u32,
|
||||||
|
field_97: u32,
|
||||||
|
field_98: u32,
|
||||||
|
field_99: u32,
|
||||||
|
field_100: u32,
|
||||||
|
field_101: u32,
|
||||||
|
field_102: u32,
|
||||||
|
field_103: u32,
|
||||||
|
field_104: u32,
|
||||||
|
field_105: u32,
|
||||||
|
field_106: u32,
|
||||||
|
field_107: u32,
|
||||||
|
field_108: u32,
|
||||||
|
field_109: u32,
|
||||||
|
field_110: u32,
|
||||||
|
field_111: u32,
|
||||||
|
field_112: u32,
|
||||||
|
field_113: u32,
|
||||||
|
field_114: u32,
|
||||||
|
field_115: u32,
|
||||||
|
field_116: u32,
|
||||||
|
field_117: u32,
|
||||||
|
field_118: u32,
|
||||||
|
field_119: u32,
|
||||||
|
field_120: u32,
|
||||||
|
field_121: u32,
|
||||||
|
field_122: u32,
|
||||||
|
field_123: u32,
|
||||||
|
field_124: u32,
|
||||||
|
field_125: u32,
|
||||||
|
field_126: u32,
|
||||||
|
field_127: u32,
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user