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. Dismiss Notice

Do we really need ecs?

Discussion in 'Entity Component System' started by calvinxie1, Oct 24, 2018.

  1. calvinxie1

    calvinxie1

    Joined:
    Sep 22, 2017
    Posts:
    5
    A bit of introduction: I worked as a gameplay engineer for several years and I think I have some experience with optimization. And I tried a few job system samples. All that does not make me an expert in anyway. I'm just throw out ideas and hopefully I can be corrected by any of the ECS experts.

    My understanding is that essentially the system in ECS is trying to replace the classic Update function in Monobehavior. Because it runs at a high frequency. What ECS tries to do is it groups up all the data that each Update function reads/writes so cache misses are minimized. All these can also be done with the manager approach. You would add the data your Update function needs to a preallocated array to the manager and process them all at once.
    In terms of the memory layout it should be very close to what ECS does, if not the same. For those event based code, like character jump, firing a weapon, can work just fine without rearrange the data layout. Actually at this point some ecs experts might object me on this.

    As for the job system, it's very useful in terms of parallelising execution. but again, it's based on the assumption that the logic is parallelisable, that is the logic is very trivial and does not have inter-data dependencies.that's why all the ecs samples you see are trivial data updates. for complicated logic, parallelisation is non trivial and probably be a topic of its own. most algorthms will have to be redesigned to run in parallele. again, it's not a problem that ecs solves. using ecs does not make your per frame logic parallelised.

    so my question is, do we really need ecs?
     
  2. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Yes we need ECS but it depends...

    Size: It depends on how large can ECS data/algorithms get before you lose the performance advantage, or can you break down any problem/algorithm into small enough of a memory footprint to gain from ECS.

    Verbosity: Then there is the fact that ECS is quite clunky and involves lots of boilerplate code that can get in the way of programming a game. For example say you can spend 1 hour putting together a Monobehaviour type game sub system or 4 hours breaking that down into a set of ECS systems that do the same job.

    Optimisation: The 80/20 rule of thumb is often used with regards to Optimisation with the watch words that you should never pre-optimize a system until you have it fully running and profiled to locate any bottlenecks. Is ECS pre/over-optimisation that is only really needed for 20% of your game.

    Reusability: Can ECS really deliver on reusable systems that can work in all your games, or build a new Unity ECS framework that makes all games and apps easy to develop.

    Debugging: ECS uses data generation, consumption and processing as the main way in which your game will work, what used to be a function call stack trace is now hunting for corrupt data in a set of interleaved systems with only where you find the bug as a hint to where it might have been generated.
     
  3. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    436
    Difference between manager approach and ECS is that systems do not care from what type of entity components come from. An example from my project, if an entity has a countdown timer on itself and does not have countdown ended component than the dedicated system will decrease its value, and if it is below 0 adds countdown ended component to the entity and that's all. Then you can have several systems that do something when the countdown ends (bomb explodes, buff/debuff disappears, cull down of weapon/ability ends, the game ends and so on). As you can see you can find a lot of such small tasks that can be generalized and performed by a single system.
     
    supron likes this.
  4. slim_trunks

    slim_trunks

    Joined:
    Dec 31, 2017
    Posts:
    41
    ECS is a framework that enables optimizations. Of course you can write your own ECS inspired logic but you'll never get the same performance boosts that you get from using ECS and the job system with Burst. And that is mainly because the first two systems place restrictions on your code that are used by Burst to greatly optimize your code.
    High frequency of updates is irrelevant I think, it is rather the erratic nature of Updating scripts sprinkled all over Heap memory that is trying to be addressed by ECS.

    Regarding the Job System, could you maybe provide are more concrete example of logic that is resistant to parallelization? Because I've experienced that more logic is parallelizable than you'd think.
    You can also always split the logic into phases than run either in parallel or only on one thread. This will give you better results without much rethinking of logic.
    Also, just running serial logic on one background thread with Burst enabled will already give you insane performance boosts.
     
  5. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,653
    It's not close. Memory and L2 linear cache is like compare turtle and rabbit.
     
  6. calvinxie1

    calvinxie1

    Joined:
    Sep 22, 2017
    Posts:
    5
    as far as i understand, ecs groups data together to minimize cache miss. a manager class can do the same. the extra optimization come from the job system, which provides a thread safe interface. burst compiler helps optimize the parallelised code. in essence, ecs does not solve any problem. if you are able to jobify a system, you could do that to the manager class as well. as Mike Acton said in his early talks, its the data layout that matters.
    as you said already, you could split up the logic into paralellissable phases. again this could be done by a manager class as well.
    another thing that some ecs advocates use is reusable systems. in real life if bullets have a transform component that is used to update their per frame positions, the same bullet move system cannot really be applied to other entity types that also have transform components. because most likely they will have a different movement pattern, like a character. at that time, some code would be needed to filter out the characters if they use the same transform component array. grouping all transforms in the same array would be a problematic data layout. splitting them into two transform arrays and updating them separately would be a better data layout.
     
    Last edited: Oct 24, 2018
  7. slim_trunks

    slim_trunks

    Joined:
    Dec 31, 2017
    Posts:
    41
    I mean of course you could roll your own "managers" solution, but I don't think there would be much benefit to that when Unity already puts a lot of effort into providing a built-in system that accomplishes the same goal.

    Yes, indeed nothing is preventing someone from only using the Job System and Burst without ECS but that wasn't the point. I just wanted to address the argument that some logic is harder to parallelize with some ideas for solutions.

    Regarding data layout. Say you have an Entity archetype that has Transform and Character Components and one that has Transform and Bullet Components. Component Data of those archetypes will be allocated in different Chunks (arrays) as they say in ECS terminology. Those can then be processed by two different Systems. So technically they are already doing that.

    You can see more about that here: https://github.com/Unity-Technologi...er/Documentation/reference/chunk_iteration.md

    I don't think the term code reuse is used within ECS in the classical sense. But that is just like my opinion man.
     
    Last edited: Oct 24, 2018
  8. GarthSmith

    GarthSmith

    Joined:
    Apr 26, 2012
    Posts:
    1,240
    ECS is a software design pattern that is a tool just like any other. If you want to do a million of something, then it's a very very good tool.

    The results below are for a most ideal test for ECS. (This is IDEAL for ECS! Actual product gains will not be this drastic.)

    I think it shows why updating the position of a million boids is such a common ECS demo.

    You cannot use reference types if you need to process a million of something every frame. It's just not possible. Too much time is spent revisiting RAM for data.

    Unity's implementation of ECS strongly encourages you to organize your data in RAM so that it is easy for the CPU to access. If you're good enough to know how your data is laid out in RAM at all times, then you can probably get the performance without using ECS. I'm not that good!


    upload_2018-10-24_13-22-26.png
     
    Last edited: Oct 24, 2018
    Vacummus, SugoiDev and supron like this.
  9. supron

    supron

    Joined:
    Aug 24, 2013
    Posts:
    67
    Both yes and no. Look. We don't need ECS to build good game. Unity is a great engine with a lot of great features and decent performance - yes, you can build large and efficient application, we tried it and it's definitely possible. But there is a wall you can't cross without changes in foundation. Today in LA, Joachim Ante gave great answer why they decided to build the whole new core in parallel to the MonoBehaviour based system. TLDW: they tried to build new transform system with data oriented design for unity. They did it. It took about 1.5 year to integrate it and fix all regressions. They got like x3 performance improvements, which is great, but it's not even close to the theoretical limit. And here we are. With ECS you can be as close to the limit as possible. With all these memory/cache optimizations and code vectorization, you can get like x100 performance improvements compared to GC heavy class oriented system.

    The thing is, you have to follow all of these rules to get that performance. Break one of them, and you get massive loss. I don't see how we could achieve this with manager based approach. The first problem I see is the lack of ability to group small chunks of the same data type into large arrays. With ECS you are forced to split your data and reuse systems as much as possible. With this approach both memory layout and parallelization seems to be easy and natural. And that's the point! Performance by default. No need to think about data layout, no need to think about multithreading, just split components, make small reusable systems and you will get outstanding performance without event thinking about manual optimizations. I know it's not easy to rebuild existing code base with ECS. We struggle with it as well. But we keep doing it and we see great potential. We learn more and more every day and I'm convinced that unity picked the right way.

    I think it's still early stage of ECS. We need more engine API for native containers, we need more "core" systems, and we need full editor support. Once we get these things, it won't differ that much from old MonoBehavoiur based approach. ECS is also great candidate for node-based visual scripting.
     
  10. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,984
    There are typically two flavors of ECS I often see (outside of Unity in the C++ world):

    Flavor 1: System owns Components: This is very similar to the Monobehaviour Manager pattern you speak of. Components on Entities just become handles to the actual data in the system (which can store the component data using any data structure it wishes). Messages can be passed between components by sending the destination component's handle and message to the appropriate system. It works really well with already existing EC architectures (like Unity's Monobehaviours) and is really good at event-driven execution. However, messaging can only be done on the main thread limiting the parallelism.

    Flavor 2: Entity Manager owns Components: This is what Unity's ECS implementation uses, though it can be easily adapted to use the other flavor. Components are data rather than data handles, and multiple systems can touch a particular component type. In addition, a system can process multiple components associated with the same entity at once, which greatly reduces the need for a messaging system. It is a very decoupled system and much harder to implement than the other flavor. Props to the engineers at Unity for absolutely nailing the foundation!

    Regarding memory layout, the manager pattern and Flavor 1 will likely produce the same memory layout assuming your manager pattern uses structs and not classes. Flavor 2 does things a little differently. Both are fast. Flavor 1 will be slightly faster due to simplicity, but you are limited with how much you can do.

    And of course if you want to use Flavor 1 in your Monobehaviour event-driven game architecture, you use Monobehaviours as the component data handle holders and the handles can either be array indices or Unity's entities (which have a single IComponentData attached). The former likely runs parallel for loops better since it can utilize smaller chunk sizes, but the latter lets you split up your IComponentData into smaller pieces which may make your code more readable, and it automatically updates your handles on addition and removal (since entities are stable). You still will have to rely on pooling of GameObjects with such an approach, but with a dedicated time point for running your systems, you can run all of your systems in parallel to each other using IJob (or if possible run them truly parallel with IJobParallelFor). It's an easy performance win!

    Of course you need Flavor 2 in order to do the optimized culling, LOD, and rendering that was done in the MegaCity demo. That's the problem Unity's ECS solves.