Skip to content

Investigate and reduce garbage collector pressure (UObject pooling?) #980

Open
@nithinp7

Description

@nithinp7

After #975, there will be significantly fewer and less frequent frame-rate drops due to Cesium for Unreal. One remaining source of frame-rate interruptions (while relatively infrequent) remains to be the garbage collector. There is often a huge (sometimes >1s hitch) while the GC is doing "reachability analysis", presumably traversing an enormous tree of UObject references, looking for objects to delete. After the reachability analysis concludes, there is a long period where each game thread tick invokes "incremental GC purging" - which is a time-allotted task that attempts to keep the frame-rate roughly at 30fps but the pressure invariably pushes the frame-rate down on average.

I'm quite confident that we are cleaning up all of our heavy resources (buffers, textures, GPU resource references, etc.). Tracing memory usage shows that texture and mesh memory naturally go up and down on their own due to our memory management, even without GC. There is a separate "Untracked" category of memory usage that Unreal shows however which usually gets up to 2-3GB-ish, at which point the GC runs. The GC bookmark in the memory trace always immediately corresponds to a sharp decrease in the untracked category (in the below case it went down from 2.4GB to 1.6GB).

The red "Untracked" category responds sharply to GC - the other categories are largely unaffected.

GCMemoryTrace

The timeline shows a 629ms hitch during the GC reachability analysis and a consistently lower frame-rate for a period after (incremental GC purge is running every frame at that point).

GCHitch

I suspect that this untracked category may be an accumulation of abandoned UObjects that we create for gltf primitive components and textures that we eventually don't need. While the render resources and buffers are freed, we have so far had no clean way to "free" UObjects short of abandoning them for the garbage collector.

If the above suspicion is true (and it would have to be investigated to confirm it), then the most sensible solution seems to be to pool UObjects (gltf components, gltf primitive components, UTexture2D objects, and any other Cesium-created UObjects I'm forgetting about). If the untracked category corresponds to abandoned UObjects, this would likely entirely eliminate the GC pressure we are seeing - if investigation shows otherwise I'm sure someone will edit this or create a new issue!

As an aside: I've seen in several places (forums, articles, etc.) that UObject pooling is the standard solution for large counts of transient objects, particularly in bullet-hell games where projectiles are spawned and destroyed almost continually.

Metadata

Metadata

Assignees

No one assigned

    Labels

    performanceImprovements to performance, including reductions in memory usage

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions