Fix performance regressions: remove TIER0, consolidate scratchpads, remove Arc trap
Key fixes based on benchmark analysis: 1. Remove TIER0 Vec-based pathfinding - O(N) linear scan was slower than FxHashMap for typical path lengths 2. Consolidate scratchpads into single AStarScratchpad struct - eliminates nested RefCell borrow overhead 3. Remove Arc wrapper from TileMap - eliminated Copy-on-Write trap causing 63ms stutters 4. Replace AHashMap with FxHashMap - FxHash is faster for small integer keys like IVec3 5. Simplify tier logic - single pathfinding function with scratchpad reuse Benchmark analysis showed: - Original: 1.95 µs/node, P99 1.1ms, Max 5ms - TIER0/TIER1 regression: 2.89 µs/node (+48%), P99 5ms (+348%) - Root causes: Vec linear scan in TIER0, nested RefCell borrows, Arc::make_mut CoW This should restore and improve performance by using simple FxHashMap scratchpad for all paths.
This commit is contained in:
+3
-9
@@ -4,12 +4,6 @@ pub const TILE_SIZE: f32 = TILE_PIXELS as f32 * PIXEL_RATIO;
|
|||||||
pub const ITILE_SIZE: i32 = TILE_SIZE as i32;
|
pub const ITILE_SIZE: i32 = TILE_SIZE as i32;
|
||||||
pub const SEED: u32 = 420;
|
pub const SEED: u32 = 420;
|
||||||
|
|
||||||
// Pathfinding tier thresholds (in tiles)
|
pub const PATHFINDER_SHORT_PATH_MAX_TILES: i32 = 100;
|
||||||
pub const PATHFINDER_TIER0_MAX_TILES: i32 = 10; // Very short paths: Vec-based
|
pub const PATHFINDER_MAX_NODES: usize = 5000;
|
||||||
pub const PATHFINDER_TIER1_MAX_TILES: i32 = 30; // Short paths: AHashMap scratchpad
|
pub const PATHFINDER_WAYPOINT_THRESHOLD_TILES: i32 = 100;
|
||||||
pub const PATHFINDER_TIER2_MAX_TILES: i32 = 100; // Medium paths: AHashMap scratchpad
|
|
||||||
// TIER3: > 100 tiles → Chunk waypoints + async
|
|
||||||
|
|
||||||
// Pathfinding configuration
|
|
||||||
pub const PATHFINDER_MAX_NODES: usize = 5000; // Max nodes before partial path
|
|
||||||
pub const PATHFINDER_ASYNC_THRESHOLD_TILES: i32 = 150; // Start async for very long paths
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+11
-56
@@ -1,6 +1,5 @@
|
|||||||
use ahash::AHashMap;
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use std::sync::Arc;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
/// Packed floor tile data for efficient storage. ~35 bytes vs 76 bytes tuple.
|
/// Packed floor tile data for efficient storage. ~35 bytes vs 76 bytes tuple.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
@@ -89,28 +88,6 @@ impl FloorTileData {
|
|||||||
self.flags &= !0b100;
|
self.flags &= !0b100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_tuple(tuple: (i32, bool, bool, bool, i32, [u32; 8])) -> Self {
|
|
||||||
Self::new(
|
|
||||||
tuple.0 as u8,
|
|
||||||
tuple.1,
|
|
||||||
tuple.2,
|
|
||||||
tuple.3,
|
|
||||||
tuple.4 as u8,
|
|
||||||
tuple.5,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_tuple(&self) -> (i32, bool, bool, bool, i32, [u32; 8]) {
|
|
||||||
(
|
|
||||||
self.id as i32,
|
|
||||||
self.can_stand_in(),
|
|
||||||
self.can_stand_on(),
|
|
||||||
self.visibly_transparent(),
|
|
||||||
self.astar_weight as i32,
|
|
||||||
self.visible_range,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Packed fixture tile data. ~18 bytes vs 48 bytes tuple.
|
/// Packed fixture tile data. ~18 bytes vs 48 bytes tuple.
|
||||||
@@ -156,28 +133,14 @@ impl FixtureTileData {
|
|||||||
pub fn can_stand_on(&self) -> bool {
|
pub fn can_stand_on(&self) -> bool {
|
||||||
self.flags & 0b010 != 0
|
self.flags & 0b010 != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_tuple(tuple: (i32, bool, bool, [u32; 8])) -> Self {
|
|
||||||
Self::new(tuple.0 as u8, tuple.1, tuple.2, tuple.3)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_tuple(&self) -> (i32, bool, bool, [u32; 8]) {
|
|
||||||
(
|
|
||||||
self.id as i32,
|
|
||||||
self.can_stand_in(),
|
|
||||||
self.can_stand_on(),
|
|
||||||
self.visible_range,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tile map with Arc-wrapped HashMaps for async pathfinding access.
|
/// Tile map using FxHashMap for fast lookups. No Arc wrapper - single-threaded access.
|
||||||
/// Uses copy-on-write: Arc::make_mut clones only if other Arcs exist.
|
#[derive(Resource, Default)]
|
||||||
#[derive(Resource, Clone, Default)]
|
|
||||||
pub struct TileMap {
|
pub struct TileMap {
|
||||||
pub floor_tiles: Arc<AHashMap<IVec3, FloorTileData>>,
|
pub floor_tiles: FxHashMap<IVec3, FloorTileData>,
|
||||||
pub fixture_tiles: Arc<AHashMap<IVec3, FixtureTileData>>,
|
pub fixture_tiles: FxHashMap<IVec3, FixtureTileData>,
|
||||||
pub item_tiles: Arc<AHashMap<IVec3, Vec<u32>>>,
|
pub item_tiles: FxHashMap<IVec3, Vec<u32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TileMap {
|
impl TileMap {
|
||||||
@@ -207,34 +170,26 @@ impl TileMap {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert_floor(&mut self, pos: IVec3, tile: FloorTileData) {
|
pub fn insert_floor(&mut self, pos: IVec3, tile: FloorTileData) {
|
||||||
Arc::make_mut(&mut self.floor_tiles).insert(pos, tile);
|
self.floor_tiles.insert(pos, tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert_fixture(&mut self, pos: IVec3, tile: FixtureTileData) {
|
pub fn insert_fixture(&mut self, pos: IVec3, tile: FixtureTileData) {
|
||||||
Arc::make_mut(&mut self.fixture_tiles).insert(pos, tile);
|
self.fixture_tiles.insert(pos, tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert_item(&mut self, pos: IVec3, entity_id: u32) {
|
pub fn insert_item(&mut self, pos: IVec3, entity_id: u32) {
|
||||||
Arc::make_mut(&mut self.item_tiles)
|
self.item_tiles.entry(pos).or_default().push(entity_id);
|
||||||
.entry(pos)
|
|
||||||
.or_default()
|
|
||||||
.push(entity_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn remove_item(&mut self, pos: &IVec3) -> Option<Vec<u32>> {
|
pub fn remove_item(&mut self, pos: &IVec3) -> Option<Vec<u32>> {
|
||||||
Arc::make_mut(&mut self.item_tiles).remove(pos)
|
self.item_tiles.remove(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_floor_mut(&mut self, pos: &IVec3) -> Option<&mut FloorTileData> {
|
pub fn get_floor_mut(&mut self, pos: &IVec3) -> Option<&mut FloorTileData> {
|
||||||
Arc::make_mut(&mut self.floor_tiles).get_mut(pos)
|
self.floor_tiles.get_mut(pos)
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_fixture_mut(&mut self, pos: &IVec3) -> Option<&mut FixtureTileData> {
|
|
||||||
Arc::make_mut(&mut self.fixture_tiles).get_mut(pos)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user