mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 23:44:38 +00:00
faster pathfinder WeightedNode::ord
This commit is contained in:
parent
8045b4eda2
commit
c7d53d6532
3 changed files with 118 additions and 13 deletions
|
@ -3,7 +3,7 @@ use std::{hint::black_box, sync::Arc, time::Duration};
|
||||||
use azalea::{
|
use azalea::{
|
||||||
BlockPos,
|
BlockPos,
|
||||||
pathfinder::{
|
pathfinder::{
|
||||||
astar::{self, PathfinderTimeout, a_star},
|
astar::{self, PathfinderTimeout, WeightedNode, a_star},
|
||||||
goals::{BlockPosGoal, Goal},
|
goals::{BlockPosGoal, Goal},
|
||||||
mining::MiningCache,
|
mining::MiningCache,
|
||||||
rel_block_pos::RelBlockPos,
|
rel_block_pos::RelBlockPos,
|
||||||
|
@ -165,6 +165,55 @@ fn bench_pathfinder(c: &mut Criterion) {
|
||||||
run_pathfinder_benchmark(b, generate_mining_world);
|
run_pathfinder_benchmark(b, generate_mining_world);
|
||||||
});
|
});
|
||||||
slow_group.finish();
|
slow_group.finish();
|
||||||
|
|
||||||
|
c.bench_function("weighted_node_le g_score", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
WeightedNode::le(
|
||||||
|
&black_box(WeightedNode {
|
||||||
|
index: 0,
|
||||||
|
g_score: 1.,
|
||||||
|
f_score: 0.,
|
||||||
|
}),
|
||||||
|
&black_box(WeightedNode {
|
||||||
|
index: 0,
|
||||||
|
g_score: 0.,
|
||||||
|
f_score: 0.,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
c.bench_function("weighted_node_le f_score", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
WeightedNode::le(
|
||||||
|
&black_box(WeightedNode {
|
||||||
|
index: 0,
|
||||||
|
g_score: 0.,
|
||||||
|
f_score: 1.,
|
||||||
|
}),
|
||||||
|
&black_box(WeightedNode {
|
||||||
|
index: 0,
|
||||||
|
g_score: 0.,
|
||||||
|
f_score: 0.,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
c.bench_function("weighted_node_le eq", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
WeightedNode::le(
|
||||||
|
&black_box(WeightedNode {
|
||||||
|
index: 0,
|
||||||
|
g_score: 0.,
|
||||||
|
f_score: 0.,
|
||||||
|
}),
|
||||||
|
&black_box(WeightedNode {
|
||||||
|
index: 0,
|
||||||
|
g_score: 0.,
|
||||||
|
f_score: 0.,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!(benches, bench_pathfinder);
|
criterion_group!(benches, bench_pathfinder);
|
||||||
|
|
|
@ -66,6 +66,7 @@ where
|
||||||
let mut best_path_scores: [f32; 7] = [heuristic(start); 7];
|
let mut best_path_scores: [f32; 7] = [heuristic(start); 7];
|
||||||
|
|
||||||
let mut num_nodes = 0;
|
let mut num_nodes = 0;
|
||||||
|
let mut num_movements = 0;
|
||||||
|
|
||||||
while let Some(WeightedNode { index, g_score, .. }) = open_set.pop() {
|
while let Some(WeightedNode { index, g_score, .. }) = open_set.pop() {
|
||||||
num_nodes += 1;
|
num_nodes += 1;
|
||||||
|
@ -94,6 +95,7 @@ where
|
||||||
if tentative_g_score - g_score < MIN_IMPROVEMENT {
|
if tentative_g_score - g_score < MIN_IMPROVEMENT {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
num_movements += 1;
|
||||||
|
|
||||||
match nodes.entry(neighbor.movement.target) {
|
match nodes.entry(neighbor.movement.target) {
|
||||||
indexmap::map::Entry::Occupied(mut e) => {
|
indexmap::map::Entry::Occupied(mut e) => {
|
||||||
|
@ -166,10 +168,13 @@ where
|
||||||
|
|
||||||
let best_path = determine_best_path(best_paths, 0);
|
let best_path = determine_best_path(best_paths, 0);
|
||||||
|
|
||||||
|
let elapsed_seconds = start_time.elapsed().as_secs_f64();
|
||||||
|
let nodes_per_second = (num_nodes as f64 / elapsed_seconds) as u64;
|
||||||
|
let num_movements_per_second = (num_movements as f64 / elapsed_seconds) as u64;
|
||||||
debug!(
|
debug!(
|
||||||
"A* ran at {} nodes per second",
|
"A* ran at {} nodes per second and {} movements per second",
|
||||||
((num_nodes as f64 / start_time.elapsed().as_secs_f64()) as u64)
|
nodes_per_second.to_formatted_string(&num_format::Locale::en),
|
||||||
.to_formatted_string(&num_format::Locale::en)
|
num_movements_per_second.to_formatted_string(&num_format::Locale::en),
|
||||||
);
|
);
|
||||||
|
|
||||||
Path {
|
Path {
|
||||||
|
@ -262,22 +267,38 @@ impl<P: Hash + Copy + Clone, M: Clone> Clone for Movement<P, M> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct WeightedNode {
|
pub struct WeightedNode {
|
||||||
index: usize,
|
|
||||||
/// The actual cost to get to this node
|
|
||||||
g_score: f32,
|
|
||||||
/// Sum of the g_score and heuristic
|
/// Sum of the g_score and heuristic
|
||||||
f_score: f32,
|
pub f_score: f32,
|
||||||
|
/// The actual cost to get to this node
|
||||||
|
pub g_score: f32,
|
||||||
|
pub index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for WeightedNode {
|
impl Ord for WeightedNode {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||||
// intentionally inverted to make the BinaryHeap a min-heap
|
// we compare bits instead of floats because it's faster. this is the same as
|
||||||
match other.f_score.total_cmp(&self.f_score) {
|
// f32::total_cmp as long as the numbers aren't negative
|
||||||
cmp::Ordering::Equal => self.g_score.total_cmp(&other.g_score),
|
|
||||||
s => s,
|
debug_assert!(self.f_score >= 0.0);
|
||||||
|
debug_assert!(other.f_score >= 0.0);
|
||||||
|
debug_assert!(self.g_score >= 0.0);
|
||||||
|
debug_assert!(other.g_score >= 0.0);
|
||||||
|
|
||||||
|
let self_f_score = self.f_score.to_bits() as i32;
|
||||||
|
let other_f_score = other.f_score.to_bits() as i32;
|
||||||
|
|
||||||
|
if self_f_score == other_f_score {
|
||||||
|
let self_g_score = self.g_score.to_bits() as i32;
|
||||||
|
let other_g_score = other.g_score.to_bits() as i32;
|
||||||
|
|
||||||
|
return self_g_score.cmp(&other_g_score);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// intentionally inverted to make the BinaryHeap a min-heap
|
||||||
|
other_f_score.cmp(&self_f_score)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Eq for WeightedNode {}
|
impl Eq for WeightedNode {}
|
||||||
|
@ -305,3 +326,37 @@ impl Default for PathfinderTimeout {
|
||||||
Self::Time(Duration::from_secs(1))
|
Self::Time(Duration::from_secs(1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn weighted_node(f: f32, g: f32) -> WeightedNode {
|
||||||
|
WeightedNode {
|
||||||
|
f_score: f,
|
||||||
|
g_score: g,
|
||||||
|
index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_weighted_node_eq() {
|
||||||
|
let a = weighted_node(0., 0.);
|
||||||
|
let b = weighted_node(0., 0.);
|
||||||
|
assert!(a == b);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_weighted_node_le() {
|
||||||
|
let a = weighted_node(1., 0.);
|
||||||
|
let b = weighted_node(0., 0.);
|
||||||
|
assert_eq!(a.cmp(&b), cmp::Ordering::Less);
|
||||||
|
assert!(a.le(&b));
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_weighted_node_le_g() {
|
||||||
|
let a = weighted_node(0., 1.);
|
||||||
|
let b = weighted_node(0., 0.);
|
||||||
|
assert_eq!(a.cmp(&b), cmp::Ordering::Greater);
|
||||||
|
assert!(!a.le(&b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,11 +8,12 @@ use azalea_core::position::BlockPos;
|
||||||
///
|
///
|
||||||
/// The X and Z are limited to ±32k.
|
/// The X and Z are limited to ±32k.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct RelBlockPos {
|
pub struct RelBlockPos {
|
||||||
pub x: i16,
|
pub x: i16,
|
||||||
|
pub z: i16,
|
||||||
/// Note that the y isn't relative.
|
/// Note that the y isn't relative.
|
||||||
pub y: i32,
|
pub y: i32,
|
||||||
pub z: i16,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RelBlockPos {
|
impl RelBlockPos {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue