-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove extra allocation in AccessFilters #14026
Conversation
Note that this breaks the model where we see this vec as an OR of AND filters. You're changing from an OR with a single AND subformula that's empty, which corresponds to a TRUE, with an OR that's empty, which instead corresponds to FALSE. IMO this makes the code even harder to understand (which seemed to be one of the motivations for this PR...). You can already see this in effect with the strange change you had to make to Edit: the TRUE/FALSE swap made me realize what's the problem with the implementation of |
The previous default is It's true that we have to be careful in a couple places because we were using
In the case of So yes the issues is that the default accumulator value of
I agree that the weak point of this PR is the However I do think the new approach is easier to reason with:
Maybe I should update the PR description to make more explicit the benefit of avoiding an allocation on every access |
Arguably, it previously was They are similar because they are both empty, but they have completly different meanings. One is the neutral element for AND (i.e. TRUE, that is all archetypes are matched), the other is the neutral element for OR (i.e. FALSE, that is no archetype is matched).
You don't necessarily have to force an equivalence between filters and logic formulas, but it does make everything much easier. The fact that
With the current implementation, yes. If we had a FALSE
If you interpret filters as logic formulas it would mean TRUE. (If you don't interpret filters as logic formulas then good luck reasoning about whether the way you implement some filters is correct or not)
When you view the filter as a logic formula however the problem is exactly that FALSE is used when it should have been TRUE or viceversa.
I firmly disagree with this, as it has the completly opposite meaning of what it naturally would have.
This is not that useful because in most situations you end up allocating that |
Closing in favor of #14352. |
…_component_access` for `AnyOf`/`Or` (#14352) # Objective - The implementation of `update_component_access` for `AnyOf`/`Or` is kinda weird due to special casing the first filter, let's simplify it; - Fundamentally we want to fold/reduce the various filters using an OR operation, however in order to do a proper fold we need a neutral element for the initial accumulator, which for OR is FALSE. However we didn't have a way to create a `FilteredAccess` value corresponding to FALSE and thus the only option was reducing, which special cases the first element as being the initial accumulator. This is an alternative to #14026 ## Solution - Introduce `FilteredAccess::empty` as a way to create a `FilteredAccess` corresponding to the logical proposition FALSE; - Use it as the initial accumulator for the above operations, allowing to handle all the elements to fold in the same way. --- ## Migration Guide - The behaviour of `AnyOf<()>` and `Or<()>` has been changed to match no archetypes rather than all archetypes to naturally match the corresponding logical operation. Consider replacing them with `()` instead.
Objective
Some parts of the AccessFilter code is currently pretty hard to understand. For example take a filter like
Or<(With<A>, With<B>)>
.Instead of having code like:
we have some strange-looking code that does something different for the first component in the
Or
filter:The reason is that if we just called
append_or
for all components, we would get afilter_sets
in the form ofvec![(), With<A>, With<B>]
, because the default value forfilter_sets
isvec![AccessFilters::default()]
.This is incorrect because then systems such as
q1: Query<Entity, Or<(With<A>)>, q2: Query<Entity, Without<A>
would fail because the two queries would not be disjoint anymore (since the first queryq1
's filter would effectively beOr<((), With<A>>
).Instead what we want is a
filter_set
in the form ofvec![With<A>, With<B>]
. The special logic for the first tuple element ensures that the existing filter_set is first replaced by the filter_set value of the first component A (to remove the emptyAccessFilters::default()
) and then we callappend_or
as per usual.I'm not entirely sure why the default
FilteredAccess
contains afilter_sets
in the form ofvec![AccessFilters::default()]
.Maybe to guarantee that
append_and
is correct (filter_sets
is in disjunctive normal form so adding 'and' terms requires at least one element infilter_sets
).Solution
filter_sets
vec![]
instead ofvec![AccessFilters::default()]
, which removes an allocation whenever we create a filter. (and whenever we clone aFilteredAccess
, which is quite frequent)AnyOf
andOr
WorldQuery
logic becomes simplified as there is no need to have special logic for the first element of the tuple anymoreTesting