From 4b64d1d1d721a6974a6a06e57749227806f6835c Mon Sep 17 00:00:00 2001 From: James Liu Date: Wed, 13 Mar 2024 18:36:03 -0700 Subject: [PATCH] Make a note about the performance of Query::is_empty (#12466) # Objective `Query::is_empty` does not mention the potential performance footgun of using it with non-archetypal filters. ## Solution Document it. --- crates/bevy_ecs/src/query/state.rs | 8 ++++++++ crates/bevy_ecs/src/system/query.rs | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 1a97e2bd70b4e..e211e3bd078b9 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -188,9 +188,17 @@ impl QueryState { /// Checks if the query is empty for the given [`World`], where the last change and current tick are given. /// + /// This is equivalent to `self.iter().next().is_none()`, and thus the worst case runtime will be `O(n)` + /// where `n` is the number of *potential* matches. This can be notably expensive for queries that rely + /// on non-archetypal filters such as [`Added`] or [`Changed`] which must individually check each query + /// result for a match. + /// /// # Panics /// /// If `world` does not match the one used to call `QueryState::new` for this instance. + /// + /// [`Added`]: crate::query::Added + /// [`Changed`]: crate::query::Changed #[inline] pub fn is_empty(&self, world: &World, last_run: Tick, this_run: Tick) -> bool { self.validate_world(world.id()); diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index e34557dbda268..fb87bb2e018ef 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -1208,6 +1208,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// Returns `true` if there are no query items. /// + /// This is equivalent to `self.iter().next().is_none()`, and thus the worst case runtime will be `O(n)` + /// where `n` is the number of *potential* matches. This can be notably expensive for queries that rely + /// on non-archetypal filters such as [`Added`] or [`Changed`] which must individually check each query + /// result for a match. + /// /// # Example /// /// Here, the score is increased only if an entity with a `Player` component is present in the world: @@ -1226,6 +1231,9 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// } /// # bevy_ecs::system::assert_is_system(update_score_system); /// ``` + /// + /// [`Added`]: crate::query::Added + /// [`Changed`]: crate::query::Changed #[inline] pub fn is_empty(&self) -> bool { // SAFETY: