My “understanding” so far is that if you have multiple lets say enemies sharing the same behavior one will benefit from using ECS or dots as a whole.
But what about a single gameobject that is checking for its distance against 20, 30 or 100s of other GameObjects. Is there any benefit in using DOTS or ECS there?
Apologies if this does not make any sense I am fairly new to DOTS. Thanks.
#Understanding the benefits of ECS better.
1 messages · Page 1 of 1 (latest)
There is a big benefit in that you're not using random access to find the position of those other game objects but instead iterating over contiguous memory. Ideally no cache misses and many awkward trips to main memory!
So does that technically mean its faster?
As the graph shows, accesses caches (L1, L2, L3) is vastly vaster than accessing main memory (this requires kernel calls).
So yes, technically faster to go through cache lines than following pointers to arbitrary parts in memory.
Thats great. Thank you so much @winged silo for taking the time
I'm very new to DOD as a whole so I'm glad to help where I can.
New as in I'm currently taking a course about it and as such have not touched DOD/DOTS/ECS before 3½ months ago ish. It does help to have people from the DOTS team actually run the lectures though 🙂
That’s awesome, where are you taking the course?
Would like to get started on DOTS myself
IT University of Copenhagen, course is completely new. And frankly will be a lot better next fall since 1.0 will be out with proper documentation 😂
Oh thought it might be an online course. Well thats great and wish you the best of luck in your course
Again thanks 😄
I wonder where in the graph access via ComponentDataFromEntity would be, as that's likely what you'd use in OPs use case of distance checking against a lot of entities from the POV of one enity.
Ehhh, I don't remember right now but there was a method for basically getting the array of the components from memory.
Then you can loop over that. ComponentDataFromEntity is entity indexed so you can't simply pointer arithmetic across it necessarily.
It should definitely be simple enough to manage though. Memory's just failing me on the functions right now
I think you can only get the array when on the main thread, unless there's something I'm not aware of.
Every individual lookup of an entity through a ComponentLookup (formerly known as ComponentDataFromEntity), you're paying the cost of a random lookup, so leaning heavily on lookups is best avoided when you can. Certain problems by their nature may require such random lookups because sometimes your entities just have 'random' relationships, e.g. 'this specific key opens that specific door'.
For collision/proximity checks (or other kinds of comparisons) between all X entities and all Y entities, you can do something like this:
- use EntityQuery.ToComponentDataArray<T> to get a copy of all T component values for the entities matching the X query
- loop over all Y's, and for each Y, loop over the array of X entity T components
This of course is an n^2 loop, so it's also something to try avoiding when you can, especially for large sets of X and Y.
For medium and large scale collision/proximity checks, you'll want to do some kind of "spatial partitioning". This is demonstrated in a new sample in the (coming very soon) updated ECS samples (https://github.com/Unity-Technologies/EntityComponentSystemSamples).
Thanks for filling in, Brian!
I think ToComponentDataArray was what I was thinking of. Haven't used it myself so I wasn't aware of it making copies.
EntityQuery.ToComponentDataArray<T> is main thread only, right? And I assume it must complete dependencies to do so?
Doesn't that mean that access via ComponentLookup/CPFE might be preferable, as that can happen on other threads? Basically, it might be better to do slower work on another thread rather than faster work on the main thread.
Although, this still relies on EntityQuery.ToEntityArray to get the entities to do component lookups inside the job of...
Yes, jobs cannot perform queries, so you can only call it on the main thread, and this triggers completion of outstanding jobs with write access to the same component type. I'd have to check, but I'm pretty sure ToEntityArray doesn't require completing any jobs (jobs can't possibly modify entity id's, after all).
In some cases, then, the cost of the sync might outweigh the cost of the entity lookups, but I'd say that's generally atypical.
Interesting, thanks!
I suppose ToEntityArray also doesn't require completing for structural changes (i.e. adding a component to an entity so that it now falls within the query), since those happen at sync points?
Yes, it's always a copy, so modifying the array contents won't actually modify the actual component values. If you want to modify the components, you'd have to copy the array values back to the components, which is a bit of a hassle and of course another cost.
True, it's not important if all you're looking for is the read access for comparison though.
Another approach would be looping over the multiple entities that you want to compare a single value to. Then the cost of ComponentDataFromEntity should be smaller and you can just copy the value into the jobs.
Not sure I understand the question: ToEntityArray just copies data without modifying anything so it doesn't make any structural changes. If you make a structural change on the main thread, the next call to ToComponentDataArray and ToEntityArray may have to redo the query to account for the possible change. Otherwise those methods may use a cached query.
If I'm comparing a set of entities against a single value, ideally I can just loop over the entities of a query. But yes, sometimes I might have an array/list of selected entities, in which case I have to do lookups for each one.
I was thinking of, say, comparing player position to positions of the different enemies. For range based aggro triggering perhaps. In such a case, it makes more sense to just loop over the enemies with the player position copied into the jobs I think
Yes, I was thinking that the ToEntityArray should wait for job completion, as the addition/removal of components in other jobs may impact the result of ToEntityArray. But then I realised that ToEntityArray does not have to wait for jobs, since the structural changes that could impact the result would only happen at sync points, not in jobs (jobs merely issue commands for structural changes to the command buffer systems). In short: structural changes don't happen in jobs, and therefore ToEntityArray doesn't need to wait for jobs 😅
also ToComponentDataListAsync exists
Yeah, that's a common case. If you every have multiple players that require the same check, you can 1. get their positions with ToComponentDataArray 2. pass the positions array into the job like you said
Yes, but in my experience the cost of scheduling takes longer than just doing it on the main thread. Not sure if that's different in 1.0, since that experience was from 0.50 iirc.
yes, the *Async variants can spare you from triggering a sync
The best advice I have for that, Rupture, is "try, profile and compare".
That's the only way to know which is faster in your specific use case. As it always is 😅
Job system scheduling overhead has generally improved since 0.50 days, but the real answer is to always 'profile if it matters'