Entity Query vs Entities For Each.

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.

Thanks.

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.

2 Likes

Thanks. Could you elaborate on why EntityQuery does random lookups? Isn’t entity data just arrays? Why would it do random lookups?

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.

7 Likes

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.

1 Like

That assumption is still correct for everything other than the WithStructuralChanges case (I haven’t dug into how that one works yet).

It is the convenience methods of EntityQuery that create copies rather than a direct view of the chunk data.

Thank you, @DreamingImLatios .

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.

Also, which internal functions do you fancy?

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. :slight_smile: But man, it would be nice to have access to some of the same tools Unity is using to create these job types.

1 Like

Maybe these are things they are going to make public in the future?

What about getting an entity query to pass into IJobChunk, and using chunk.GetNativeArray?

Do either of these return copies, or are they just pointers.

Just pointers. This technique is fast and is the basis behind many of the chunk iteration scaffolds.

2 Likes

Awesome thanks!