I haven’t posted in a few days because I’ve been working on almost purely programming related tasks, which don’t lead to pretty screenshots. I’ve been improving the efficiency of the spatial partitioning scheme, and terrain draw args sorting.
Today though, I (re)wrote an object pool class.
Garbage collections are expensive on the compact CLR, they occur whenever 1MB of garbage is created, and cause noticeable stutter in the game when they happen. For this reason, I simply try not to generate any garbage.
Why we can’t just choose to have collections occur every 16MB or something, I don’t know.
I’ve written custom pools in various places in this project, because creating an efficient generic pool is tricky. Have a look at swampthingtom’s generic object pool. It uses a struct as a container for each pooled item and can only pool types that meet the
new() constraint (public parameterless constructor, and no abstract classes allowed).
Having to write a pool for every type though is slowing me down, so I spent some time today thinking about a good design for a generic object pool.
Giving up abstract classes (which is what swampthingtom does with his pool) is going a little too far for me. Also, a significant performance cost is associated with the use of that pool, which makes it less than ideal if you are using it to allocate hundreds of objects or more.
Here’s another approach, which is minimalist, and still uses the new() constraint. This pool is clearly faster, but offers no way to iterate over over the collection of active pooled objects, and again, no abstract classes or constructors with parameters allowed. It’s also basically just an unsafe version of Stack
So I wrote a pool that partitions an array into two parts, the first part for live objects, the second for dead objects. Then I effectively do what the compact CLR does with dead objects — sweep across the dead objects once in a while, and compact them into the second part of the array. As for object initialization, I pass the pool a Func delegate that does the initialization. Delegates are a little slow, but in this case they are called rarely — in fact, they should be called never, if I pick the right pool sizes. The code is barely tested so far and might benefit from another small optimization or two, but I’ll be sure to post it soonish. As usual, I’m also not fond of the idea of posting good code this far from the competition deadline.
By the way, I fought with stackoverflow to try to get some discussion going about ways to iterate over the pool without generating garbage. Most people did not want to hear about avoiding garbage creation. Eventually Eric Lippert came to the rescue and defended my question.