IJobForEach and Entities.ForEach appears to give you one thing to work on at a time, but that means there must be a Burstable, invisible loop over it.
The single component you requested could span multiple chunks due to using components/tag components liberally. (liberally = to benefit flexibility of EntityQuery so it could always have some way to select chunks that serves the need of the game, while keeping data duplication minimum.)
Two things concerning performance regarding memory are the cache line size and prefetching. I am wondering should I care about limiting tag component to make the invisible part more “in the same linear piece”? Because these two is to be the default accessing method I have to start caring about this.
Assumptions
- Requesting one type of component.
- 64 bytes of cache line size.
- 16kb * N fixed chunk size added for each archetype and N times that it goes over chunk capacity is not a problem, only care about performance.
Here’s what I guess are happening :
- There is a routine to link up (but not copy, since that would be expensive each schedule?) multiple linear array of a component, from different chunks.
- So occassionally when it jumps from chunk to chunk I may have at most 64 bytes of unused cache memory because physically they are far apart by 16kb fixed chunk size and there are a lot of empty space to cross most of the time.
- If each component is one integer (4 bytes), then it is about 16 things that could be modified faster because they got added for free to the cache line per request.
- Therefore imagine if I have about 30~50 things to iterate (not millions), having them all in the same chunk could make big difference (this is a very hot code path) as opposed to they are fragmented in 4 chunks of 10~15 things each because of my tag usage.
- Prefetching could be a problem also, is there anything added so compiler know how to cross the chunk and get next data ahead of time as if it is just one linear array? Or by being careful about my archetype I am helping the prefetching too?
When NOT using tag component liberally in order to make things in the same chunk could really pay off, I have to complement it with data duplication occassionally which reduce maintainability. (Now you have to remember to modify 2 same things instead of one, etc.) Have a version of the same data, but arranged in a different way just for fast access.
For example I may have all cars planned with exactly the same archetype so they are all in the same chunk, they all run each frame and need calculation. (e.g. some car doesn’t have booster and don’t need booster stats, but I attach the component anyways for the sake of same archetype but simply ignore it)
But equally hot codepath requires only the red cars to check for collision so I would also like all the red cars separated. (Maybe by ISharedComponentData that has an enum color Red) But now if I do that, the previous “all cars” routine will have to cross from non-red to red cars chunk. This is just 1 cross but in real game it could get out of hand if we use tags/ISharedComponentData in a way that benefits EntityQuery “too much”.
But if these chunk crossing is negligible then I can use the tag the same way I was using.