For example, running a System with a ForEach on some entities, or getting an entity query of said entities and then passing that array to a job and iterating it with a normal for-loop.
What are the advantages and disadvantages of each, and in what scenarios would I prefer one over the other? Any general tips? Most importantly, performance-wise.
I would appreciate if you could put it in the context of an RTS with 10 thousand + units.
Entities.ForEach iterates over the component data directly. That is very fast, especially if you use the SystemBase codegen Entities.ForEach and don’t use WithoutBurst or WithStructuralChange or managed components.
EntityQuery.ToComponentDataArray creates a copy of the components. That is useful if you are then going to march over that array in some O(n^2) algorithm, but is otherwise slower. EntityQuery.ToEntityArray requires the use of ComponentDataFromEntity which is doing random lookups to find the Entity index even if the chunks are iterated over linearly.
So there are four data structures in Entities. Most people are familiar with the chunks and the arrays inside of them. A fair number of people are familiar with blobs. There’s some magic for managed and shared components which are stored in object arrays. But the fourth is the least talked about because Unity nearly completely abstracts it away. There’s an array, ordered by Entity index, which contains where the entity of that index is located in the chunks. So if an Entity has index 2 and version 3, then at index 2 of this special array, there is some metadata about which chunk the entity is in and which index in the chunk the entity is at. ComponentDataFromEntity uses this special array when doing its lookups.
This special array is what allows Entity values to be stable handles. But at the same time, that also means the order of entities in this special array is completely different from the order in chunks. Therefore, when you do a lookup using ComponentDataFromEntity, you are accessing both data structures, and one of those accesses has to be a random access.
EntityQuery is just a mechanism for getting the chunks with specific components. It just happens to have convenience methods for baking out the data to or extracting data from NativeArrays.
Hmm…Thank you for this thread. I’v been away for a bit, and I assumed that Entities.ForEach was just a convenient syntax change, but that it was still using EntityQueries under the hood.
I’d like to learn more about what it means, that Entities.ForEach “iterates over the component data directly”. Is there documentaion, or a forum post, which explains this in more detail? Or any particular source I can look at to help understand it? Many thanks.
I’ve been digging through the Entities.ForEach source this morning - it looks like they do construct an EntityQuery behind the scenes. I’m guessing your original comment was mainlt refering to how ToComponentDataArray() creates a copy?
Also: Man, DOTS has a whole lot of useful, but internal, functions!
Yes I was. I suppose that “array” in the original post is ambiguous and could have been referring to a NativeArray, but I assumed the convenience methods.
I’ve been running into new ones each day, while looking into how to write custom job types. Today, it was functions dealing with Safety Handles, like EntityQuery.GetSafetyHandle(), & EntityManager.SafetyHandles.
Also today: functions that Unity uses to initialize some of their jobs: ComponentSystemBase.GetEntityQueryInternal(), & EntityQuery._EntityComponentStore
I’m sure they have reasons in mind why those are locked away for the time being. But man, it would be nice to have access to some of the same tools Unity is using to create these job types.