I need to verify my understanding of how Unity organizes Chunks in memory. Would the following all be true?
In Unity ECS, Chunks are blocks of memory which store the Component Data, for entities of the same archetype (which all have the same components).
Adding or removing a Component from an entity changes the entity’s archetype. This will always cause Unity to move the entity’s Components to a different Chunk in memory.
Moving entities between Chunks May mean expanding or shrinking the chunk. This is all managed automatically by Unity.
Here’s the part I’m confused about:
A System can request a collection of entities that all have the specified component types. This will return all entities with the specified component types, EVEN IF some of those entities have other component types which are NOT in common.
If #4 is true, then that would mean a System may often be iterating over entities of different archetypes, which therefore exist in different chunks of memory. That would eliminate the sequential reading of memory, from which ECS derives its largest perf benefit. CPU cache misses seem like they would be unavoidable, and potentially very frequent.
Thanks to anyone who can verify what I’ve written here, or tell me what I’m misunderstanding.
3 cache misses is certainly tiny, but what about iterating over every different kind of entity with a Position Component?
The other reason I ask, is that I’ve heard Joachim and one other unity dev recommend adding components to an entity as a way of sending messages. Reactive systems would then catch those new components and respond to them.
But that could seriously cut down on archetype cohesion. Imagine if you had 200 entities which originally only had a ‘B’ component. Now, one update later, 10% could also have a ‘D’ component, and 10% more might have an ‘E’, etc. the types or numbers of permutations could be large and unpredictable.
And then there’s any system which needs to access Components from a foreign entity (one that’s not in the current iteration group). For example, each entity you’re currently iterating over might need to check some component data of a separate, ‘target’ entity.
It seems like all of those things would cause cache misses, and all of them are pretty common situations in game code.
Should I not be using Unity ECS unless my game features thousands of similar objects, without much interaction between entities (like in Unity’s recent demos)?
It would cause 3 misses because CPU cannot prefetch. With the same chunk it can prefetch equal to cache line size while you are working on the current item. (64 bytes per line?)
ECS’s memory scheme certainly at its worst when you have like 1 or small entity number per archetype and many archetypes. If you have more than 1 entity in the archetype it is still better than full OOP, since that should be non contiguous at all in common iteration over the same class instances.
For tagging it is not just causing cache miss, you also get the ability to selecting only the chunk you want to work on with ECS chunk query. It is like you get a dynamic grouping system automatically rather than maintaining your own or iterating through all items and use if to find the item to work on. Maybe you could benefit from that still.
And still ECS provides good interfacing to C# jobs that make it possible to use the previously idle worker threads. Without ECS you would be copying data for the job to work on.
Also if you need faster iteration through all B component you should prepare your own NativeArray collected from chunks so they have no chunk borders and became contiguous, depending if it worths or not.
I really shouldn’t be so curmudgeonly. Ultimately I’m a huge fan of ECS, and want it to succeed. I want these problems to be solved, and I’m thrilled that such a force of talented engineers like Unity is taking on such difficult problems.
I want to keep in mind that it’s still very early days for Unity ECS, and there will be time to address some concerns.
For my part, I will settle in for the ride, and work as hard as I can to adopt their approach. I also want to find ways to apply some of the benefits I love from event-driven code to an ECS architecture.
Like tertle said, each entity’s Components are only in a single chunk.
If there was a copy in multiple chunks, you’d have to figure out how to propegate changes across all the copies. Besides being odd, that would break the pattern of linearly reading memory, which lets Unity take advantage of CPU prefetching.
It would make linearly reading memory faster if you read from the right chunk because the data would be contiguous, but copying the data would be too slow, and so it makes sense that it is not done that way.