Search Unity

Official Introducing the DOTS Best Practices guide (0.16, 0.17)

Discussion in 'Entity Component System' started by SteveM_Unity, Feb 9, 2021.

Thread Status:
Not open for further replies.
  1. optimise

    optimise

    Joined:
    Jan 22, 2014
    Posts:
    2,129
    I mean there's another high performance unmanaged system inherit from ISystemBase.

    I take a set of systems from system group at mid range android mobile device. Just this simple system group already takes 0.13ms. Not to mention other system group.
    upload_2021-8-9_21-33-10.png



    When the system don't find any matching Entities, seems like it takes 0 ms to run.
     
    Last edited: Aug 9, 2021
  2. desertGhost_

    desertGhost_

    Joined:
    Apr 12, 2018
    Posts:
    260
    So, is there a noticeable difference in the cost of house keeping of archetype changes if I change a large number of components on a given entity vs a single component on a given entity when entity command buffer playback occurs?

    To clarify, outside of the cost of the playback itself of a entity command buffer is there a difference in cost of archetype change housekeeping if I change many components vs a few on the same entity?

    In terms of archetype change house keeping is it better to try to have more changes (across one or more chunks) in a single entity command buffer playback vs fewer changes in multiple entity command buffer playbacks?
     
  3. l33t_P4j33t

    l33t_P4j33t

    Joined:
    Jul 29, 2019
    Posts:
    232
    i just realized that you can always split one job into two, or three jobs. by storing state in between.
    which is pretty bad. i was doing this in allot of places inadvertently.

    it does no good as it won't get parallelized, it decreases chunk utilization, multiplies schedule cost, clutters up the inspector and generally makes everything more confusing.
    its best to (only) store inter frame persistent state

    if you have something like a movement system. probably not good to split it into many different parts like gravity, velocity, jump, camera effects, rotation, running, sliding, etc.

    i can't be the only one who fell for this anti pattern
     
    Last edited: Aug 30, 2021
  4. TieSKey

    TieSKey

    Joined:
    Apr 14, 2011
    Posts:
    225
    I wouldn't call that an anti-pattern. Design patterns are usually there to avoid bugs, making code flexible enough but at the same time easier to test and maintain.

    I get what u are saying. From a pure performance pov, unifying all the logic that consumes/edits the same data is faster, and that's what we want to achieve with ECS (for anyone that doesn't care about ultra performance please avoid ecs...).

    But as far as design patterns go, having all in the same job is more of the "anti pattern". To be fair, almost all ECS in itself is an anti-pattern. It might be nicer, clearer for some but usually, having some data defined in one place which is stored in another place that then is modified by X amount of scripts/systems from any part of your software is a maintenance hell for any decently sized project. Ofc games are not necessarily "big" in terms of pure logic code and sometimes ultra high performance is necessary.
     
    Last edited: Aug 30, 2021
  5. desertGhost_

    desertGhost_

    Joined:
    Apr 12, 2018
    Posts:
    260
    Why wouldn't this get parallelized? If you have multiple systems writing to a shared state component through many jobs that use schedule parallel, the job system will simply schedule parallel jobs in a ordered fashion. Code modularity is achieved by having many different jobs / systems. Ideally, a system / job would handle the smallest possible part of a task to maximize code reuse. Unless a set of your jobs / systems literally use all of the same components, then they should stay as individual jobs / systems. However, performance limitations do have to be considered. There is a large cost to scheduling a job. If a set of your jobs do share the same components, then combining them into one big job will better overcome the inherent cost of scheduling a job.
     
  6. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    It depends whether you actually need to access those separate "components" / data.
    If you don't need to perform any extra ops on "Movement" data outside "Movement" logic, I'd avoid spliting them as systems / jobs. Otherwise, its still beneficial.

    Also, it highly depends on the numbers of entities that perform logic.
    In cases where entity count is high - it may be actually faster to break down due to vectorization / burst and parallel processing. As an alternative - break logic steps into methods, and just keep them as part of single job. And if entity count rises - you'd just move those methods to the different system just as easily. (ref / out are pre-defined anyway)

    So profiling comes first. Knowing game limitations / entity counts is important.

    Scheduling jobs cost is something that will get improved over time. So that shouldn't be that big of a deal. (unless you're at the end of the production)
    As for the inspector - use SystemGroups. Its pretty straight-forward to make everything tidy and in order.
     
    SteveM_Unity likes this.
  7. l33t_P4j33t

    l33t_P4j33t

    Joined:
    Jul 29, 2019
    Posts:
    232
    i think a good rule to follow is something like this:
    have as few components as possible. have as many jobs as you can as long as you aren't required to transfer temporary state between them.
    movement would be the biggest culprit for this.
    unity's example is pretty good where they have a single 1.5k+ line job, instead of splitting jump, ground check, rotation, gravity, velocity, slope sliding, crouching, etc.

    i wouldn't ever go back to non dots. i think it is in every way better, however there's a massive opportunity cost in learning it.
    you could instead learn new advanced artist techniques & workflows that would lead to more faster better assets
     
    Last edited: Aug 30, 2021
  8. TieSKey

    TieSKey

    Joined:
    Apr 14, 2011
    Posts:
    225
    It depends a lot on what u want to do. If it works for your games and makes things easier to achieve, go ahead, full steam.

    But in general terms, a data oriented approach is a lot less flexible than OOP (that's why we invented OOP after decades of doing, well, data oriented stuff in C or older languages with only struct arrays and static methods). And in particular, unity ecs is still quite green so the mono behavior approach is a lot more likely to fulfill a game's needs.
     
  9. l33t_P4j33t

    l33t_P4j33t

    Joined:
    Jul 29, 2019
    Posts:
    232
    dots currently is allot less mature, yes. and that unfortunately currently means monobehaviour is far superior, you just can't base most projects on something so comparatively new, untested and lacking in resources.

    but in a few years, i see it completely surpassing monobehaviour.
    pure oop is not very good when dealing with large web of references and constantly changing state.
    you need some kind of a data base that you query.

    anyhow systems still have all the oop, and you build the ecs representation from monobehaviour gameobjects.

    you can build something that very closely mirrors the traditional monobehaviour workflow but in dots, and still use some or none of its aspects.

    you'd have a number of advantages:
    -FindObjectOfType<>() is too prohibitively expensive. entityqueries are faster and there's more control
    -update after / before is simpler, more control.
    -update doesn't always happen. you can require conditions to be met.
    -gameobjects / components both require garbage collection AND manual deletion, which kind of defeats the purpose of garbage collection.
    -adding components, removing components, destroying & creating entities is faster
    -scene loading is faster.

    the biggest thing i like about dots is more automatic variable management and more objects living in the project folder.

    it can feel very c like sometimes, ill give you that.
    maybe the c proponents are onto something
     
    Last edited: Aug 31, 2021
  10. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    451
    You are making bad assumption that procedural programing equals DOD. For me ECS is way more flexible then any traditional OOP approach. And ECS does not mean DOD, you can make ECS with OOP, but DOD is much better fit for it. With OOP eventualy you will hit a wall where your interfaces are not good fit for requirements that have changed and adapting to that change is not that easy. Usualy OOP entusaist it this kind of situation say becasue it was not good enoug OOP, but for me it is just en excuse to defend it.
     
    desertGhost_ and JesOb like this.
  11. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,109
    One of oldest statement of OOP -> Prefer composition over inheritance
    https://en.wikipedia.org/wiki/Composition_over_inheritance

    Most of current languages lack good tooling to make this happen and have greet tools to make opposite. C# is not exception.

    ECS is concept that make this OOP principle to be first class and most ease to use and make inheritance more hard to use. So with ECS you can write better OOP (According to Composition Over Inheritance) code than with clean C#

    Current state of Unity ECS is not ready and it is not easy to use today, but C# 8-10 make things better and Unity ECS is still in Experimental Phase so things will go better :)
     
  12. TieSKey

    TieSKey

    Joined:
    Apr 14, 2011
    Posts:
    225
    ECS with OOP is MonoBehavior. Yes, as I said in this same thread the general ECS term is more than unity DOTS is, in this last couple posts I was using ECS to talk about unity entities in particular.

    Any well designed, extensible old style procedural C piece of software ended up being pretty similar to a DOD. You had arrays of structs encapsulating/grouping related data and methods that would receive them as params to operate on that data.
    Years later people said "hey, lets put those procedures inside the structs themselves to reduce the mess".
    Some more years later those structs evolved into classes with inheritance, interfaces, etc.

    A DOD using ECS (as implemented by unity DOTS) looks great until you start needing entities with 40 components and over 50 systems operating on them, you lose track of what is happening, where and when. That's why be chose some decades ago to pay significant performance costs in order to maintain relevant data and behavior more or less unified and encapsulated in objects. I'm not saying it's perfect, just that it is by nature more flexible than a DOD ECS.

    You have some very basic examples in this thread were implementing a skill/actions system becomes quite convoluted really fast. Interface/Inheritance + dynamic binding is the OOP language level implementation of the "workarounds" proposed here to achieve that flexibility with DOTS (function lookup tables, a giant switch or 100 systems waiting for a singleton entity).

    I don't want to favor any tech, they are all tools, and as tools, they are good for some tasks, bad for others. A DOD ECS as implemented by DOTS is an incredible tool to extract all performance from hardware, but not so much to make implementing and maintaining a big piece of software easy. (Again, most games are not necessarily big in terms of code). And well, DOTS is experimental and unstable so ofc I won't recommend it unless u know exactly what u need. (I'm using hybrid renderer myself).

    -------

    Now, DOD with OOP is quite interesting and I use myself. But that is offtopic :p
     
    MINORLIFE likes this.
  13. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    451
    No it is not. It is just some entieties with components. Key part of ECS is that componets have no heavy logic. Ability to query entities based on specified set of componets is also very importent part.

    I agree that many things are easier to implement using OOP, but ease of acces to data and ease of rearagment of those data is a big plus for me. I think that entity with 50 components would be an anti-pattern. You can always split your entity into several entities. Of course OOP has its place but I think most of games will benefit from ECS. Testability of ECS is greater then OOP. You do not need any mocks to test your systems, just prepera input data run logic and check output.
    I would like to see that. If you use definition of DOD presented by Mike Acton then it is hard to do :)
     
  14. TieSKey

    TieSKey

    Joined:
    Apr 14, 2011
    Posts:
    225
    GameObjects are the "entities", MonoBehaviors are the "components" (they are officially called like that by unity) and u can run queries by component such as FindComponentsInChildren<>() etc. The only "odd" part is the "systems" being the "update" method of your MonoBehaviors although nothing stops us from having monos with only data and monos with only logic.

    Having several entities holding data relevant/belonging to a logical thing, a character for example, is very very tricky since now u have an implicit relationship between them.

    I'm not familiar with Mike Acton but a quick search seems to point to the guy talking about performance. I'm not particularly interested in DOD for its potential to be implemented in a super hardware efficient way but rather for the simplicity of network syncs.
    Basically, store data in a flexible/compositional way inside an object to keep coherency/avoid entity managers/keep some encapsulation. Look at minecraft architecture for an example (which to be fair, could use some of the performance).
     
  15. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    451
    But you can't do find game objects with set of components and a specially game objects without specific components. So no it is not an ECS, it is just a Component design pattern.
     
  16. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    867
    At this point I have ported about 250 classes from OOP to functional programming using static classes. Almost all the effort has been changing from reference types to value types. I think I have less then 10 systems. Arbitrary database query are not something that is going to benefit most programmers.

    Basically one giant mess of nested functions calling other functions calling other functions. I only use ECS to store persistent data.
     
    Last edited: Sep 1, 2021
    MINORLIFE and JesOb like this.
  17. TieSKey

    TieSKey

    Joined:
    Apr 14, 2011
    Posts:
    225
    Hey, that's an interesting migration. It would be really nice if u can find the time to do a short write up once u finish the process, highlighting the good/bad things and performance differences if any.
     
    JesOb likes this.
  18. l33t_P4j33t

    l33t_P4j33t

    Joined:
    Jul 29, 2019
    Posts:
    232
    what kind of performance improvements are you seeing, from the direct conversion?
     
  19. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    867
    I was already getting 100+ FPS on my target system before switching. I switched in order to leverage DOTS physics and because I wanted to switch from client server to deterministic lockstep networking. The later now being in doubt because it is not clear if Burst will support deterministic floats.

    It is very hard to compare ECS to GameObjects. Unity's old physics was very limited, and I switch from using my own homemade physics engine, that allowed roll back to, Unity's DOTS physics. All my code is now multithreaded. I am sure my project will be faster using ECS, but it was never CPU bound. It was always the GPU that was the limiting factor.

    I have my project about 60% ported and since everything references everything else it is still massively broken. According to GitHub I started porting full time on Feb 1 and I have had 768 commits since that date. Working 8-12 hours a day 7 days a week. I hope to finish next January or February.

    ECS does force you to use it in a very strict way. It is not like GameObjects where you have freedom to do whatever you want you basically only have one way to do stuff. Honestly it is fine. But without polymorphism you will either end up with some duplicated code; or you will need more functions that call other functions in many cases all the way down to blittable types. Which is more the route I took.

    My project depends on Burst supporting deterministic floats. I Will benefit from ECS either extremely or else to one degree or another.
     
    Last edited: Sep 1, 2021
  20. l33t_P4j33t

    l33t_P4j33t

    Joined:
    Jul 29, 2019
    Posts:
    232
    you can achieve polymorphism with a switch on an enum. i figure its a completely irrelevant cost that wont even be paid by the main thread. function pointers are a thing too.

    although i haven't really needed polymorphism. i had allot of code duplication when dealing with different worlds. the server world would ostensibly have the same systems but with small differences. i ended up saying screw it and just slapped some if statements like if (client) playsound();

    i have allot of polymorphism during conversion. which is then not needed at runtime.

    oop is allot more constrained when it comes to significant structural runtime changes or modding, as you can't change the inhertance hierarchy or fields of a class
     
    Last edited: Sep 1, 2021
  21. TieSKey

    TieSKey

    Joined:
    Apr 14, 2011
    Posts:
    225
    Well, u can always inherit the class u want to mod and add what u need. Ofc the original game has to give u a way to replace the reference to the object so u can inject the derived/moded one (or force it with reflection). In general yes, for modding/runtime changes composition is easier to deal with than inheritance. Although u could argue than finding and replacing a given system with a modded one is not actually easier (afaik).

    I take advantage of both, I use a class hierarchy to declare "things" with certain properties I know are always needed (like rendering stuff, names, ids) and can override methods when needed. At the same time this classes are also data/component containers where I can add stuff (kind of adding components to an entity) that then systems defined elsewhere can pick up and do their stuff with. Not super performant but quite flexible (and I can always use the reference to global struct array strat that unity ECS uses where performance is really really needed).
     
  22. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,109
    With Unity ECS it is easy. Just use write group on your modded entity and old systems will not touch it
     
  23. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    867
    Using enums only makes sense if the data is in the same component and you are trying to separate it out. I find myself doing something like this when that is not the case. The less similar the behaviors are the less enums makes sense and the more spaghettification you end up with. In this case I would separate out mass and speed into a component but that is not always logical when your code becomes complex.

    Code (CSharp):
    1.         public static float EnergyFunction(in physicalProjectileGrenadeComponent)
    2.         {
    3.             return EnergyFunction(physicalProjectileRocketComponent.massKG, physicalProjectileRocketComponent.speedMS);
    4.         }
    5.  
    6.         public static float EnergyFunction(in physicalProjectileBulletComponent)
    7.         {
    8.             return EnergyFunction(physicalProjectileRocketComponent.massKG, physicalProjectileRocketComponent.speedMS);
    9.         }
    10.  
    11.         public static float EnergyFunction(in physicalProjectileRocketComponent)
    12.         {
    13.             return EnergyFunction(physicalProjectileRocketComponent.massKG, physicalProjectileRocketComponent.speedMS);
    14.         }
    15.  
    16.         private static float EnergyFunction(float massKG, float speedMS)
    17.         {
    18.             return 0.5F * massKG * speedMS * speedMS;
    19.         }
     
    Last edited: Sep 1, 2021
  24. l33t_P4j33t

    l33t_P4j33t

    Joined:
    Jul 29, 2019
    Posts:
    232
    don't really get what you're saying there, maybe post an example?
    seems like you could benefit from component systems which can iterate over monobehaviours vs system base.
    or something like class MyIComponent : BaseClass, IComponentData.
     
  25. l33t_P4j33t

    l33t_P4j33t

    Joined:
    Jul 29, 2019
    Posts:
    232
    you could use an interface


    or better yet, you can just use an aggregate


    i think dots definitively requires a change of frame of mind and allot of practice, to fully utilize it.
    my early dots code was pretty bad, despite having significant general experience then
     

    Attached Files:

    Last edited: Sep 1, 2021
    SteveM_Unity likes this.
  26. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    867
    I really think putting interfaces on components is a horrible idea. Like I said in the case I gave I would separate out mass and speed into a component but that is not always logical when your code becomes complex.

    I have total thrown away OOP and everything is now a function in my code base.
     
    Last edited: Sep 1, 2021
  27. l33t_P4j33t

    l33t_P4j33t

    Joined:
    Jul 29, 2019
    Posts:
    232
    how come, what's wrong with interfaces?
    i don't really see a scenario where oop would be a better solution and dots is subpar.
    the only case i've seen where dots abstractions are truly lacking and code duplication is required is in netcode, where unity needs to serialize, deserialize and predict any arbitrary component. in that case, unity has a custom solution involving code generation

    C# generators should patch that hole in dots functionality
     
  28. TieSKey

    TieSKey

    Joined:
    Apr 14, 2011
    Posts:
    225
    Well that part of my code is in the server which runs outside of unity so it's not really relevant to the thread.

    Although I'd love to have some sort of "IMonoComponent : IComponentData" interface for structs that I can add to regular mono behaviors and then use either from an ECS system or directly through the object. (I think it could be possible once default interface methods are implemented)

    To be fair I could implement that myself by holding a reference to an entity in an object and adding some extension methods :p
     
  29. l33t_P4j33t

    l33t_P4j33t

    Joined:
    Jul 29, 2019
    Posts:
    232
    component system can iterate over plain old monobehaviours and gameobjects, as if they were entities.
    default interfaces probably won't actually add any new functionality. you can already achieve default implmenetation for interfaces through generic extension methods
     
  30. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    867
    That is not burstable all code needs to be burstable.

    Doesn't this cause boxing? In any case I don't like the smell of interfaces on components.
     
    Last edited: Sep 1, 2021
    MINORLIFE and Krajca like this.
  31. SteveM_Unity

    SteveM_Unity

    Unity Technologies

    Joined:
    Nov 21, 2017
    Posts:
    41
    davenirline and PhilSA like this.
Thread Status:
Not open for further replies.