Skip to content

Commit

Permalink
Add reflect path parsing benchmark (#9364)
Browse files Browse the repository at this point in the history
# 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.
  • Loading branch information
nicopap authored Aug 25, 2023
1 parent a788e31 commit 7b0809b
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 0 deletions.
5 changes: 5 additions & 0 deletions benches/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
94 changes: 94 additions & 0 deletions benches/benches/bevy_reflect/path.rs
Original file line number Diff line number Diff line change
@@ -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,
);
},
);
}
}

0 comments on commit 7b0809b

Please sign in to comment.