Designs of an entity component system
Another approach that I found pretty promising and I've used in a project of mine (see EnTT
on GitHub) is based on sparse sets.
I used them within the component pools, to keep track of which entity has a component associated and what's its slot.
Major benefits are:
You get for free a small array of all the entities that have the specific component (see here for further details) and this gives you a great boost in terms of performance when you iterate over them.
It keeps at a minimum the number of components actually assigned. Moreover, components are all kept compact in memory.
Cache misses are reduced at a minimum, for you pay only for what you use. In other terms, you get only those components that are actually assigned to an entity and all of them are near to each other in memory, no holes at all.
You get it at the price of an extra array with max length equal to the number of entities for each component pool (note that usually those arrays are smaller in a real world software).
Benchmarks shown that performance are far better than the ones of a well known entity component system based on bitmask descriptors (see the link above for further details). I've also verified that memory pressure is more or less the same, for you get rid of the array of bitmask descriptors but you introduce a set of mini arrays within the component pools.
Iterations over sets of entities when looking for multiple components can be highly improved too with a trick: find the shortest set and iterate over its entities (a really fast operation), then verify if the n-th entity has the other components and eventually return it.
Benchmarks proved that it's still faster than the bitmask based design on dense sets (where each entity has all the components). In case of sets not so dense (that is a reasonable assumption for a real world software), performance are definitely better than bitmask based solutions.
Finally, differently from solution #4, no dynamic cast is required in this case.
The whole thing gives you something I'd call an entity component registry. Systems can be defined as lambdas that capture the registry or functors to which you can pass the registry. There is no need to register systems with the registry itself.
I hope you got the idea behind this implementation.
If you need more details, feel free to ask.