3 ms·
ECS is doing a bit more than looping over arrays, though. In modern production ECS systems there's often the case where a System acts over two or more Component
by ratww 5y ago
ECS is doing a bit more than looping over arrays, though. In modern production ECS systems there's often the case where a System acts over two or more Components is very common, and looping itself is not good enough for those cases (because you get O(n^2), O(n^3), O(n^4), etc, etc complexity), so it's nowhere near as simple as that.
Like I said, it's possible to implement anything that is computable in any Turing-complete language. The fact that there are some ECS implementations in OOP languages speaks more to the popularity of those languages, rather than their capabilities or suitability to having ECS implemented in it. Of course ECS is going to need classes in languages that don't allow structs. Of course ECS is going to need classes in languages that don't allow freestanding functions. But that's doesn't mean that everything that is implemented in an OOP language can be considered just another OOP pattern.
ECS is a more foundational way of organising code, in the same space as OOP itself.
One of OOP features is encapsulation: data and methods living together. ECS enforces the complete opposite: complete separation. Another OOP feature is inheritance. ECS removes inheritance and replaces with composition.
If one says that ECS a just another design pattern, then it's only fair to say that OOP is also just a design pattern in itself, since I could implement OOP itself in C (see Linux Kernel), Haskell or ECS if I wanted.
- kaba0 5y ago> If one says that ECS a just another design pattern, then it's only fair to say that OOP is also just a design pattern in itself, since I could implement OOP itself in C (see Linux Kernel), Haskell or ECS if I wanted. Well, it is? What do you mean by looping itself? How is having a struct dissimilar to having an object iterated, especially with C++ essentially having barely any difference between a struct and a class?
- ratww 5y ago> Well, it is? If you say so we can agree on that, yes. :) > What do you mean by looping itself? I mean that that ECS is not merely looping over an array like GP claimed. In production ECS, Systems normally act on more than one kind of Component, so "iterating over the array" of components is not feasible. Iterating over entities kinda kill the performance and data locality too, so you need something smart closer to "materialised SQL views" to get performance. > How is having a struct dissimilar to having an object iterated, especially with C++ essentially having barely any difference between a struct and a class? I never said it was. I just said that merely using a "class" doesn't make something an OOP pattern, especially if the implementor has no choice over which programming feature to use.
- kaba0 5y agoThanks for the details! Though wouldn’t using arrays of structs kill the performance the same way as array of objects do (stack-allocated ones)? So, one would essentially have to have a SOA, or — the OOP abstraction: a class of arrays that enclose this hot loop of code, making the implementation opaque to a calling code.
- ratww 5y ago> Though wouldn’t using arrays of structs kill the performance the same way as array of objects do (stack-allocated ones)? Not really. Components are long-lived so they're not allocated on the stack. And contiguous arrays of structs/objects is the best case for cache locality, that's the entire performance case of ECS.
- kaba0 5y agoA simple struct/object having in order an int, a string pointer and another int having 3 different things happening to them can in some case be worse than having all the ints next to each other, etc. ISIISI… vs III…SSS…III.. So for cache locality the latter is the best option.
- meheleventyone 5y agoBest way to think about ECS is a generic pattern to help make things data oriented. It’s not necessarily going to be optimal though. It’s SoA over the Entity rather than individual struct members which might in a more tuned setup be more performant. This is also where ECS as a pattern falls down (from the simplistic ideas about them being performant) because keeping components in packed arrays is only one part of making them efficient. The biggest issue is making sure your entity data is best organised for the most common data access patterns. For example you can’t actually store data in big blocks of single component type but need to consider the shapes/archetypes (common sets of components) that make up an Entity. For each archetype we can store the components packed in arrays so that they can be iterated contiguously. That way Systems can visit all of the archetypes that contain the components they care about and iterate straight through them whilst minimising cache misses. This approach obviously falls down the more heterogeneous your Entities are. It also incurs a cost when a component is added or removed from an Entity so makes some common ECS patterns more expensive. There are other approaches with different trade offs. But this is also pretty academic for most games which don’t have the entity count or gameplay complexity to actually require this level of performance anxiety.
- jayd16 5y ago> System acts over two or more Components is very common, and looping itself is not good enough for those cases (because you get O(n^2), O(n^3), O(n^4), etc, etc complexity), so it's nowhere near as simple as that. Am I missing something? Sort your data structure and quickly loop over them? Why would you ever do things at n^4 complexity? > then it's only fair to say that OOP is also just a design pattern Yup. It is. No reason to treat it like a religion throw it away because you can't force
- ratww 5y ago> Am I missing something? Yeah. You're missing the part where I said ECS is not just looping. It's clearly also not just sorting, if you know how ECS is structured. You need either indexing, like databases, or you need to watch for changes and cache them before usage, like in SQL materialised views. If you want to keep discussing the basics of ECS I strongly suggest searching at least how it is structured, its goals, and how it works.
- jayd16 5y agoPerhaps I should because it seems like you simply listed ways to sort. My understanding is any ECS implementation would always spend the time bookkeeping to prevent O(n^4) or other combinatoric explosion. Systems run every frame while entities are spawned much less often, if at all between load screens. Nested linear searches as you suggest would also blow out any locality improvements, which made the suggestion even more confusing to me. Unity's implementation is deeply focused on keeping component data tightly packed so systems can quickly loop over them with SIMD, or even batch them across other threads. They keep entities sorted in buckets by component list, eg entities with components A, B, and AB are grouped separately (but still as arrays of the underlying component data). They can trivially pass organized sets of arrays to any system that may need them. No linear searching is required as the arrays of separate component data are naturally matched. If a system needs to operate on A and B, the paired arrays are ready made. ...but what do they know?