Friday 10 July 2015

DOD-ECS Memory Management


Data-Oriented software designs can generally be loosely compared to relational databases in terms of function, however they are usually closer to associative arrays in terms of form.

FireStorm's ECS internal data layout is governed by a template class (inherited by all Systems) which is called ObjectPool<T>, indicating that it's a container for data of a specified Type.
The container implements a kind of memory micro-manager, or mini-heap, of objects of Type T - it's a kind of  array manager which has several useful features, such as the ability to grow, shrink and defragment the array. The objects it holds can be class object instances or pure structures, and in the case of class objects, high frequency creation and destruction is quite cheap, as the Pool recycles objects from a linkedlist of 'free objects' whenever possible, and only uses Operating System allocation api when 'under duress'. The implication is that the array can contain 'holes' - objects marked as 'dead' remain in the array memory, which can be undesirable, depending on what we're using the array for.

In my case, the class was specifically written to act as a container for Components, and to act as a base for all Systems. I was going to need to detect and handle 'highly fragmented' arrays, but the motivation to do so did not come until I had to handle the tricky situation of Parented Transforms (that deserves a post to itself).

FireStorm identifies unique Components by their Component ID - the flat Storage Index of the element in its Pool. Since elements don't normally move in memory, these don't normally change value, even if the container is reallocated (grows or shrinks), which would not always be true if Pointers were used to identify Components.
On the other hand, Defragmenting or Sorting a Pool does modify Component IDs - but most Systems don't need to do this often (or ever).


In addition to the object pool, each System inherits three other items of interest:
- a message queue mechanism for inter-system messaging
- a multimap that associates a subscriber EntityID with a ComponentID (pool index).
- a multimap that holds the reverse associations, accelerating certain operations

Multimap was chosen because an Entity may own more than one Component of the same Type.
The reversemap was added last, simply because we almost never want to iterate the Components of a given System 'by Entity' - although sometimes, we do.


No comments:

Post a Comment