diff --git a/Cargo.lock b/Cargo.lock index 4bfde36d5..b56d65055 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1639,6 +1639,7 @@ dependencies = [ "ahash", "approx", "bevy", + "criterion", "de_core", "de_index", "de_map", diff --git a/crates/pathing/Cargo.toml b/crates/pathing/Cargo.toml index 061e3afed..5045084ad 100644 --- a/crates/pathing/Cargo.toml +++ b/crates/pathing/Cargo.toml @@ -26,3 +26,8 @@ futures-lite = "1.11.3" [dev-dependencies] ntest = "0.8.0" +criterion = "0.3" + +[[bench]] +name = "pathing" +harness = false diff --git a/crates/pathing/benches/pathing.rs b/crates/pathing/benches/pathing.rs new file mode 100644 index 000000000..627d80771 --- /dev/null +++ b/crates/pathing/benches/pathing.rs @@ -0,0 +1,75 @@ +use std::{ + fs::File, + io::{BufRead, BufReader}, + path::PathBuf, +}; + +use bevy::prelude::GlobalTransform; +use criterion::{ + criterion_group, criterion_main, AxisScale, BenchmarkId, Criterion, PlotConfiguration, + Throughput, +}; +use de_index::Ichnography; +use de_map::size::MapBounds; +use de_pathing::create_finder; +use glam::Vec2; +use parry2d::{math::Point, shape::ConvexPolygon}; + +const MAP_SIZE: f32 = 8000.; + +fn load_points(number: u32) -> Vec { + let mut points_path: PathBuf = env!("CARGO_MANIFEST_DIR").into(); + points_path.push("test_data"); + points_path.push(format!("{}-points.txt", number)); + let reader = BufReader::new(File::open(points_path).unwrap()); + + let mut points = Vec::with_capacity(number as usize); + for line in reader.lines() { + let line = line.unwrap(); + let mut numbers = line.split_whitespace(); + let x: f32 = numbers.next().unwrap().parse().unwrap(); + let y: f32 = numbers.next().unwrap().parse().unwrap(); + points.push(MAP_SIZE * Vec2::new(x, y)); + } + points +} + +fn load_entities(number: u32) -> Vec<(GlobalTransform, Ichnography)> { + load_points(number) + .iter() + .map(|p| { + let ichnography = Ichnography::new( + ConvexPolygon::from_convex_hull(&[ + Point::new(p.x - 10., p.y + 10.), + Point::new(p.x - 10., p.y - 10.), + Point::new(p.x + 10., p.y - 10.), + Point::new(p.x + 10., p.y + 10.), + ]) + .unwrap(), + ); + (GlobalTransform::identity(), ichnography) + }) + .collect() +} + +fn create_finder_benchmark(c: &mut Criterion) { + let mut group = c.benchmark_group("create_finder"); + let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); + group.plot_config(plot_config); + + for num_entities in [100, 1000, 10_000, 100_000] { + let entities = load_entities(num_entities); + + let bounds = MapBounds::new(Vec2::splat(MAP_SIZE)); + + group.throughput(Throughput::Elements(1)); + group.bench_function(BenchmarkId::from_parameter(num_entities), |b| { + b.iter(|| { + create_finder(bounds, entities.clone()); + }); + }); + } +} + +criterion_group!(benches, create_finder_benchmark); +criterion_main!(benches); diff --git a/crates/pathing/src/finder.rs b/crates/pathing/src/finder.rs index 70f74c389..96ca1532d 100644 --- a/crates/pathing/src/finder.rs +++ b/crates/pathing/src/finder.rs @@ -15,7 +15,7 @@ use crate::{ }; /// A struct used for path finding. -pub(crate) struct PathFinder { +pub struct PathFinder { /// Spatial index of triangles. It is used to find edges neighboring start /// and end pints of a path to be found. triangles: RTree, diff --git a/crates/pathing/src/lib.rs b/crates/pathing/src/lib.rs index d824cafba..c57225de2 100644 --- a/crates/pathing/src/lib.rs +++ b/crates/pathing/src/lib.rs @@ -50,4 +50,4 @@ mod systems; mod triangulation; mod utils; -pub use systems::{PathingPlugin, UpdateEntityPath}; +pub use systems::{create_finder, PathingPlugin, UpdateEntityPath}; diff --git a/crates/pathing/src/systems.rs b/crates/pathing/src/systems.rs index 285cc49d5..1e7cd8190 100644 --- a/crates/pathing/src/systems.rs +++ b/crates/pathing/src/systems.rs @@ -232,7 +232,11 @@ fn check_update_result( } /// Creates a new path finder by triangulating accessible area on the map. -fn create_finder(bounds: MapBounds, entities: Vec<(GlobalTransform, Ichnography)>) -> PathFinder { +// This function has to be public due to its benchmark. +pub fn create_finder( + bounds: MapBounds, + entities: Vec<(GlobalTransform, Ichnography)>, +) -> PathFinder { debug!( "Going to create a new path finder from {} entities", entities.len() diff --git a/crates/pathing/test_data/.gitattributes b/crates/pathing/test_data/.gitattributes new file mode 100644 index 000000000..56638dc9f --- /dev/null +++ b/crates/pathing/test_data/.gitattributes @@ -0,0 +1 @@ +*-points.txt filter=lfs diff=lfs merge=lfs -text diff --git a/crates/pathing/test_data/100-points.txt b/crates/pathing/test_data/100-points.txt new file mode 100644 index 000000000..019c227ff --- /dev/null +++ b/crates/pathing/test_data/100-points.txt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24936eebe0ca78446f5239d1acb3f907eb552e99dc92868050862461536fe847 +size 3852 diff --git a/crates/pathing/test_data/1000-points.txt b/crates/pathing/test_data/1000-points.txt new file mode 100644 index 000000000..dc68016ee --- /dev/null +++ b/crates/pathing/test_data/1000-points.txt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a080dd671a3509a537495c6b60eb4214b3965ec2509eb531a805898a564678a7 +size 38523 diff --git a/crates/pathing/test_data/10000-points.txt b/crates/pathing/test_data/10000-points.txt new file mode 100644 index 000000000..3865bcde8 --- /dev/null +++ b/crates/pathing/test_data/10000-points.txt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:69172922c8d27883ef81a7abcc100fdcf16a196d34c5d965589f5aae7224e81b +size 385388 diff --git a/crates/pathing/test_data/100000-points.txt b/crates/pathing/test_data/100000-points.txt new file mode 100644 index 000000000..e1d5ef8be --- /dev/null +++ b/crates/pathing/test_data/100000-points.txt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0e6c6343e353e433508cff7f61bb3fe96b6820c72823a12d28fed8070c0891c +size 3854078