From f570f52aa2e9faf6272a9aede562dbcc142787d5 Mon Sep 17 00:00:00 2001 From: SpecificProtagonist Date: Fri, 13 Sep 2024 17:47:24 +0200 Subject: [PATCH] Optimize observer unregistration (#15150) # Objective Fixes #14980 ## Solution Only iterate over archetypes containing the component. ## Alternatives Additionally, for each archetype, cache how many observers are watching one of its components & use this to speed up the check for each affected archetype ([implemented here](https://github.com/bevyengine/bevy/commit/55c89aa0334975642bbd8978d97fe66c4970078a)). Benchmarking showed this to lead only to a minor speedup. ## Testing There's both already a test checking that observers don't run after being despawned as well as a regression test for the bug that necessitates the check this PR optimizes. --- crates/bevy_ecs/src/archetype.rs | 2 +- crates/bevy_ecs/src/observer/mod.rs | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index 15e962291c..5b56e1cff4 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -766,7 +766,7 @@ pub struct Archetypes { /// find the archetype id by the archetype's components by_components: HashMap, /// find all the archetypes that contain a component - by_component: ComponentIndex, + pub(crate) by_component: ComponentIndex, } /// Metadata about how a component is stored in an [`Archetype`]. diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 2571157302..1c1507fdce 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -456,14 +456,17 @@ impl World { if observers.map.is_empty() && observers.entity_map.is_empty() { cache.component_observers.remove(component); if let Some(flag) = Observers::is_archetype_cached(event_type) { - for archetype in &mut archetypes.archetypes { - if archetype.contains(*component) { - let no_longer_observed = archetype - .components() - .all(|id| !cache.component_observers.contains_key(&id)); + if let Some(by_component) = archetypes.by_component.get(component) { + for archetype in by_component.keys() { + let archetype = &mut archetypes.archetypes[archetype.index()]; + if archetype.contains(*component) { + let no_longer_observed = archetype + .components() + .all(|id| !cache.component_observers.contains_key(&id)); - if no_longer_observed { - archetype.flags.set(flag, false); + if no_longer_observed { + archetype.flags.set(flag, false); + } } } }