-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Contiguous access #21984
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
base: main
Are you sure you want to change the base?
Contiguous access #21984
Conversation
|
crates/bevy_ecs/src/query/filter.rs
Outdated
| /// - The result of [`ContiguousQueryFilter::filter_fetch_contiguous`] must be the same as | ||
| /// The value returned by every call of [`QueryFilter::filter_fetch`] on the same table for every entity | ||
| /// (i.e., the value depends on the table not an entity) | ||
| pub unsafe trait ContiguousQueryFilter: QueryFilter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this? Is there a reason we can't use QueryFilter<IsArchetypal = true>?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do solely IsArchetypal = true then filter may exclude only some entities from a table (because a table can have many archetypes 'attached' to it), which wouldn't allow us to return a whole slice (effectively rendering the whole thing obsolete).
If we do solely IsDense = true and IsArchetypal = true then there might be a case when QueryFilter::filter_fetch returns false for some tables (which it doesn't have to exclude via other methods) and if we don't have a counter part (ContiguousQueryFilter::filter_fetch_contiguous) then the results of the contiguous fetch and the non-contiguous might be different.
I am not fully sure though whether this kind of query filter should exist in the first place. And I am pretty certain rust doesn't support QueryFilter<IsArchetypal = true> generic, so having ContiguousQueryFilter (at least without any additional methods like by ReadOnlyQueryFilter) is necessary in my opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah so basically because IS_ARCHETYPAL=true requires for an implementer to make QueryFilter::filter_fetch always return true (which makes ContiguousQueryFilter::filter_fetch_contiguous meaningless) and because we check whether the query is dense, it effectively means that ContiguousQueryFilter is the same as ArchetypeFilter (It excludes archetypes and because the query is dense, more precisely it excludes tables, which was ContiguousQueryFilter's meaning), thus I removed ContiguousQueryFilter in favor of ArchetypeFilter.
This pr just enables slices from tables to be returned directly when applicable, it doesn't implement any batches and it doesn't ensure any specific (other than rust's) alignment (yet these slices may be used to apply simd).
This pr doesn't deal with any alignments but (as of my understanding) you can always take sub-slices which would meet your alignment requirements. And just referring to the issue #21861, even without any specific alignment the code gets vectorized.
No, the returned slices do not have any specific (other than rust's) alignment requirements. |
|
The solution looks promising to solve issue #21861. If you want to use SIMD instructions explicitly, alignment is something you usually have to manage yourself (with an aligned allocator or a peeled prologue). Auto-vectorization won’t “update” the alignment for you – it just uses whatever alignment it can prove and otherwise emits unaligned loads. From that perspective, a contiguous slice is already sufficient; fully aligned SIMD is a separate concern on top of that. |
Objective
Enables accessing slices from tables directly via Queries.
This pr is a draft to get feedback on the design.
Fixes: #21861
Solution
One new trait:
ContiguousQueryDataallows to fetch all values from tables all at once (an implementation for&Treturns a slice of components in the set table, for&mut Treturns a mutable slice of components in the set table as well as a struct with methods to set update ticks (to match thefetchimplementation))And a method
as_contiguous_iterinQueryItermaking possible to iterate using these traits.Testing
sparse_set_contiguous_querytest verifies that you can't usenext_contiguouswith sparse set componentstest_contiguous_query_datatest verifies that returned values are validbase_contiguousbenchmark (file is namediter_simple_contiguous.rs)base_no_detectionbenchmark (file is namediter_simple_no_detection.rs)base_no_detection_contiguousbenchmark (file is namediter_simple_no_detection_contiguous.rs)base_contiguous_avx2benchmark (file is namediter_simple_contiguous_avx2.rs)Showcase
Example
Benchmarks
Code for
basebenchmark:Iterating over 10000 entities from one table and increasing a 3-dimensional vector from component
Positionby a 3-dimensional vector from componentVelocitybypass_change_detection()methodUsing contiguous 'iterator' makes the program a little bit faster and it can be further vectorized to make it even faster
Things to think about
offsetparameter inContiguousQueryData