Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Selecting entities and basic workflow when working with ECS

Discussion in 'Entity Component System' started by glad, Dec 27, 2018.

  1. glad

    glad

    Joined:
    May 10, 2014
    Posts:
    76
    Hi!

    I need to understand why ECS is faster.

    Several things I can't get:
    1. In onUpdate we are selecting some entities by some filter each frame! How can it be fast ? I wonder how it is working under the hood. Even if you have some complex dictionaries and the search is fast enough you do that each tick!!! I can't wrap my mind here. It would be great if you say couple of words about that.
    2. I've looked videos of "tiny project". That's pretty good explanation of ECS approach. But the thing I can't really get is how you distinguish the one entity from another? The author of the video said you can't and showed approach with empty components so-called tags: playerTag, enemyTag. So if this is the only way then we back to the 1st question: why this is faster ?
    3. About the chunks. As far as I understood the chunk is a peace of memory where your component data stored. Probably the chunk size is limited by OS that allow you to reserve for your component data. Am I right here ?
    4. In the "tiny project" there was't real physics. The collision detection was manual. What about the physics provided by physics engines(Box2D/PhysX). Will they work ? Where their place are in ECS ?

    Thank you in advance.
     
  2. dartriminis

    dartriminis

    Joined:
    Feb 3, 2017
    Posts:
    157
    1. The filtering (or querying) doesn't happen on each update, it happens when you create a new ComponentGroup, or a new Archetype (which can happen when you add or remove components from entities).
    2. To distinguish entities from other entities, simply add components to your entities and then create an EntityArchetypeQuery (see ChunkIteration).
    3. Chunk size is constant (16KB). Ideally, chunks should be roughly the size of your processors L1 cache, which is usually 32KB but can also be 16KB for some processors.
    4. The unity team is slowly converted certain features of unity from MonoBehaviour land to ECS land. Right now they just have the transform system and some rendering systems. I think they had a slide somewhere detailing some of the other future systems they will be implementing, and I'm certain physics will be one of them.

    Let me know if this helps :)
     
    FROS7 likes this.
  3. glad

    glad

    Joined:
    May 10, 2014
    Posts:
    76
    Thank you for such good answer!

    1. Hm.. I guess if you delete entity or add/remove component from entity in runtime onUpdate should also be called. If so then Huge spikes could be when you do that because lets say you have 100 systems in your game. Each have that querying. If the game want to spawn 50 units or other entities. So probably for each system there will be such querying and it could be a spike. Huge spike.

    3. Any thought why is it so small ? :)
     
  4. dartriminis

    dartriminis

    Joined:
    Feb 3, 2017
    Posts:
    157
    1. I don't think that would necessarily be the case, and i think I may have given a slightly ambiguous description. The ECS internals keep track of the unique ComponentGroups that you create. When you initially create the ComponentGroup, it performs the query to find the matching Archetypes and saves the results. Then, when you call CreateArchetypeChunkArray, it iterates the matching Archetypes and joins all their Chunks in to one ArchetypeChunk array.

    Adding and destroying entities (of existing archetypes) may end up adding an additional chunk, but that just get's tacked on to the Archetype's Chunk list (so no querying happened).

    When you add or remove components, you are effectively changing the Entity's Archetype (Archetypes are based on the entity's component types). If the new archetype already exists, then that entity is simply moved to a free chunk of that archetype. If the new archetype doesn't exist, then a new Archetype will be added to ECS and it will iterate and add the archetype to the query results of any matching ComponentGroup.

    I hope that explains things better.

    3. In short: The processor's L1 cache (32KB on most modern processors) is orders of magnitude faster than its L2 and L3 cache, and main memory. If an entire chunk can be stored inside the L1 cache, and you don't need to access data outside of that chunk (which should be true in most typical cases), then you shouldn't experience any cache misses while performing your update logic on a particular chunk. This is part of the performance bonus with ECS.
    ECS and chunks are part of a larger design concept called Data-Oriented-Design. There are some additional articles and videos (Some of which are presented by Unity's own Mike Acton) that can explain this much better than i can here.
     
    glad likes this.
  5. glad

    glad

    Joined:
    May 10, 2014
    Posts:
    76
    Awesome answer! Thank you so much!

    Now I understand that I know nothing about ECS. I need to learn about ECS more deeply. I need some time to investigate more, read some docs. I have some questions still, but I think I will ask them when I'll be ready enough to understand the basis. Because everything I watched in videos carries out a very superficial manner.

    About the caches: there are many chunks.. Probably you would need to have access to many of them.. But still.. I think it is my lack of knowledge about ECS internals.. So need some time.. I would post in here lately(may be in a week or two).

    Again, many thanks for your answers !
     
  6. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    857
    I had some misconceptions about chunks myself and knowing how they work in Unity is important, so I'll share some things I know.

    Each combination of components is stored in its own chunk. When you are trying to get all ComponentData of a specific type usually you get it from entities from a lot of different chunks. If you ask for everything with Health in RTS game you get all the units, buildings, destructible objects all from a bunch of different chunks.

    When iterating through chunk, switching between chunks can or will result in a cache miss. But when iterating inside the same chunk you are going linearly through L1 cache and it is very fast. So it is better not to divide entities into many archetypes.

    Each SharedComponent's value creates it's own chunk as if it is a different component.

    Each time you change entity's archetype by adding or removing components you are actually moving these entity's memory (from each component array) to another chunk. So it is quite slow compared to other things.

    Burst compiler speeds up iteration (and most operations) over entities 100x or more. It is faster to iterate over a 1000 entities to find one with a particular value, than it is to tag it and make it move to another chunk and filter it that way. You can do millions of iterations each frame for a tiny cost and it probably will be on a job on worker threads.

    Since iteration is so cheap I personally dont change my entities' archetypes and if I need it to receive an event I just have the component of that event on potential recipients at all times and change this component's value to raised whenever it is raised. Besides adding and removing components always happens on the main thread, and this way I can avoid that.

    Some benchmarks here: ( https://forum.unity.com/threads/fla...-over-component-data-int.599200/#post-4008556 )

    Since raycasts are also jobified I now do all my projectiles in pure ecs with potentially thousands of raycasts each frame for a tiny cost.


    So fast because it's linear memory access with rare cache misses.
    Because of Burst's voodoo.
    Because of easy multithreading.
     
    Last edited: Dec 28, 2018
    andrew-lukasik and Antypodish like this.
  7. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,131
    This is a bit misleading...in this case it depends only on how many entities can fit in 1 chunk of that archetype, could very well be 1 chunk only


    Edit: Yes, much clearer now after your edit.
     
    Last edited: Dec 28, 2018
  8. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    857
    I clarified with the example "If you ask for everything with Health in RTS game you get all the units, buildings, destructible objects all from a bunch of different chunks."

    I'm not talking about the amount of memory, but about the fact that when we search for specific components we usually get them from many different archetypes (chunks).

    Oh, I see, my use of the word archetype might have been misleading. I edited it.
     
    Last edited: Dec 28, 2018