Add reflect path parsing benchmark (#9364)

# Objective

We want to measure performance on path reflection parsing.

## Solution

Benchmark path-based reflection:
- Add a benchmark for `ParsedPath::parse`

It's fairly noisy, this is why I added the 3% threshold.

Ideally we would fix the noisiness though. Supposedly I'm seeding the
RNG correctly, so there shouldn't be much observable variance. Maybe
someone can help spot the issue.
This commit is contained in:
Nicola Papale 2023-08-25 16:30:26 +02:00 committed by GitHub
parent a788e31ad5
commit 7b0809b532
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 99 additions and 0 deletions

View file

@ -47,6 +47,11 @@ name = "reflect_struct"
path = "benches/bevy_reflect/struct.rs"
harness = false
[[bench]]
name = "parse_reflect_path"
path = "benches/bevy_reflect/path.rs"
harness = false
[[bench]]
name = "iter"
path = "benches/bevy_tasks/iter.rs"

View file

@ -0,0 +1,94 @@
use std::{fmt::Write, str, time::Duration};
use bevy_reflect::ParsedPath;
use criterion::{
black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput,
};
use rand::{distributions::Uniform, Rng, SeedableRng};
use rand_chacha::ChaCha8Rng;
criterion_group!(benches, parse_reflect_path);
criterion_main!(benches);
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];
fn deterministic_rand() -> ChaCha8Rng {
ChaCha8Rng::seed_from_u64(42)
}
fn random_ident(rng: &mut ChaCha8Rng, f: &mut dyn Write) {
let between = Uniform::try_from(b'a'..=b'z').unwrap();
let ident_size = rng.gen_range(1..128);
let ident: Vec<u8> = rng.sample_iter(between).take(ident_size).collect();
let ident = str::from_utf8(&ident).unwrap();
let _ = write!(f, "{ident}");
}
fn random_index(rng: &mut ChaCha8Rng, f: &mut dyn Write) {
let index = rng.gen_range(1..128);
let _ = write!(f, "{index}");
}
fn write_random_access(rng: &mut ChaCha8Rng, f: &mut dyn Write) {
match rng.gen_range(0..4) {
0 => {
// Access::Field
f.write_char('.').unwrap();
random_ident(rng, f);
}
1 => {
// Access::FieldIndex
f.write_char('#').unwrap();
random_index(rng, f);
}
2 => {
// Access::Index
f.write_char('[').unwrap();
random_index(rng, f);
f.write_char(']').unwrap();
}
3 => {
// Access::TupleIndex
f.write_char('.').unwrap();
random_index(rng, f);
}
_ => unreachable!(),
}
}
fn mk_paths(size: usize) -> impl FnMut() -> String {
let mut rng = deterministic_rand();
move || {
let mut ret = String::new();
(0..size).for_each(|_| write_random_access(&mut rng, &mut ret));
ret
}
}
fn parse_reflect_path(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("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),
&size,
|bencher, &size| {
let mut mk_paths = mk_paths(size);
bencher.iter_batched(
|| mk_paths(),
|path| assert!(ParsedPath::parse(black_box(&path)).is_ok()),
BatchSize::SmallInput,
);
},
);
}
}