A Data Structure You Haven't Heard Of
Imagine you are implementing an application, or in this case a game, that consists of many objects with dynamic lifetimes that also reference each other. To get rid of pointer related issues such as cache misses and invalid pointers you could store all objects in a big array and have them reference each other through indices, like this:
|
|
This is sometimes good enough but this wouldn’t be a blog post unless we could improve on it, right? In games you often want to avoid dynamic memory allocation so the arrays are usually fixed size, meaning we don’t resize them and instead reuse indices. The problem with the above approach is that if an Enemy
at index 2
is destroyed we might spawn a new one at that same index. That would cause the Missile
to follow a totally different enemy which might not be what we wanted, resulting in a bug.
Generational Arenas
We can solve the lifetime tracking issue and get constant time O(1)
insertions, lookups and deletes by using a data structure called Generational Arena (sometimes referred to as Generation Pool). A Generational Arena is similar to a Slot map and uses the same concept as the data array but we introduce a generation counter for each index. Every time data is deleted at an index, we increase the generation counter at that index. When asking for data at an index, we check if the generation at the provided index is the same as the generation in the arena. If the data was deleted (and possibly reused), the generation would be higher, meaning the arena would return null.
If we wanted to get the data at index 2 but provide a generation of 3 we would get null because the data has been removed since we got our handle. However, if we provide a generation of 4 we would get the data because the data has not been removed meaning our view of that data is still up to date.
To make insertion an O(1) operation, a freelist is used. It’s basically just a stack that we push to when data is removed and pop from when data is added. Sometimes, you’d want dynamic resizing of your arena but that’s not something I need for my use case. Implementing that and optimising the freelist is left as an exercise for the interested reader.
Here’s the implementation and as promised, it’s less than 50 lines of code. The number of lines would probably increase a bit if you do the suggested exercise above but not by much.
|
|
Here’s a silly example of how it could be used:
|
|
As you can see it’s a really useful data structure with constant time operations that does not take many lines of code to implement.
Enjoy!