Entity Database/Component Storage
Contents
Component Type Records
Criteria
- Memory usage
- Query and filtering efficiency
- Maximum number of component types
Container-based Approach
If the entity is treated as a bag of components, a natural way of storing the components is putting them in a bag-like container such as a hashmap. The components are indexed by their typeid
s and typically live together with the type info in the same hashmap.
Memory Usage
In this approach, each component demands one std::type_index
for the type identification. This wrapper class around std::type_info
is typically composed of a pointer to the latter, therefore requires at least 64 bits on a 64-bit machine. The total memory usage should also include that demanded by the hashmap.
Efficiency
Determination of the existence of a particular component type is done by first calling typeid(T)
, where T
is the type of the component. This is usually done during compilation so the actual cost only happen during queries. The result std::type_info
is then hashed and queried with the hashmap to see whether it presents in the container. To query multiple types of components, the query must be done linearly to the number of component types.
Maximum number of component types
As long as the memory is adequate, the hashmap can store any number of components.
Bitmask Approach
A compact way to track the presenting types of components is using a bitmask with one bit for each type of component. This approach is obviously faster than querying with typeid
but poses an visible limitation that the maximum numbers of component types must be known during compilation assuming that the length of the bitmask is fixed. Another problem is the assignment of bits to each type of component, which may require some compilation units to know all the types of components in order to generate consistent bitmasks.
Memory Usage
This representation is quite compact and uses a fixed amount of memory for each entity decided by the maximum number of component types. The size is usually <math>\ceil{NumMaxComponents/64} * (64 / 8)}</math> on 64-bit machines.
Efficiency
Testing the existence of a particular component type is done in three steps: getting its bit position, build the bitmask representing its existence, test with the entity's bitmask. This is usually done in several instructions depending on the length of the bitmask, but always has the same cost. Querying for multiple types involving performing the first two steps for each type and using logical OR to build the logical disjunction mask. The building of testing mask is usually done during compilation, so the actual cost only consist of testing with the entity's bit mask.
Maximum number of component types
The length of the bitmask limits the number of component types can be used in the system. Since increasing its size also increases the memory usage and reduces performance, it demands a careful design of components and systems to make the application efficient, which might not be a bad thing after all.