On Thursday, 20 December 2012 at 22:45:10 UTC, Benjamin Thaut wrote:
Did you do something special about the GC?
Does it run at all or do you somehow pool your objects?

Kind Regards
Benjamin Thaut

I do use the GC; however, in general, I avoid using it for anything
that gets called very often; e.g. once per entity per frame.
Code that calls the GC once per frame is not a problem.

The game entities are composed of components (disjoint in memory;
every component type has a manually allocated array of all components of
 that type, and an entity has indices into these arrays), which
are structs. When some action triggers an entity spawn (in SpawnerSystem, one of subsystems working on entities), an EntityPrototype - (struct of components, not yet an entity) is either reused or allocated. I preallocate a large number of these. Once the time comes for the entity to appear in the game, components from the EntityPrototype are copied to their respective arrays. These arrays are once again preallocated, and if they run out of space, they are enlarged. These are actually "segmented" (arrays of arrays), so I never reallocate, just allocate a next segment.

Overall, memory allocation is no longer a concern.
Actually, GC was never a problem (I always worked with the rule -
if called many times per frame, don't use GC). However, manual allocation
_was_ a problem; until I rewrote most of the code to preallocate.
The worst case I had with the GC was about 10% of total time;
now both GC and manual allocation is below 1%. Actually; just
profiled (two-level game)- GC about 0.2% ; malloc & friends about 0.4%.

In some cases I use pooling for structs that get created/destroyed often; these usually are arrays stored within components, e.g. WeaponComponent, which might have any number of weapons. I do this in a very hacky way, using an allocator template argument for my (custom) collections; this allocator
is generally static and specific for type being allocated.

Reply via email to