Skip to content

Performance / multithreading compatibility improvements

Latest
Compare
Choose a tag to compare
@VergilUa VergilUa released this 31 Jan 17:05
· 11 commits to master since this release

This version will break previous version pooling logic. Make sure to read upgrade guide or use only in new projects.
Minimum required version of Unity is 2019+ onwards

Changelog:

  • Added compatibility for multithreaded read / writes by removing automatic parent handling;
  • Removed auto-creation of "pool" game objects;
  • Added support for using .Pool at editor time;
  • Editor and runtime performance improvements;
  • Removed some PoolingExt extension methods to reduce number of misuses / wasted ops.

Upgrade guide and logic behind changes:

Short version:

  1. By default pooler no longer manages T.transform.SetParent(...) state.
    Meaning in .Decommission you should restore previous transform parent state (if modified).
  2. .Pool(prefab, +position, +rotation, +parent) methods were removed.
    Use .Pool(prefab) instead of those calls and access transform directly to modify position, rotation, scale or parent.

Long version:
In previous versions, pooler internally was accessing transform to "return" spawned gameobject under its created root.
While this is handy for keeping hierarchy view tidy, this has also proven to be an issue when spawning / using large quantities of objects. Since objects / transforms cannot be read / written in parallel, this slows down both native engine side, and IJobParrallelForTransform jobs.
Hence why it was removed. You can read more on subject here and here.

Moreover, even on objects which parents were not changed - pooler still accessed transform, and modified parent, wasting potential ops.
This change should reduce both loading / spawning time and improve runtime IGenericPoolElement object manipulation speed for engine systems and domain code.

While it was handy to have .Pool(prefab, +position, +rotation, +parent) extensions (identical on par with .Instantiate), this has also proven to be an issue.
Hiding this logic internally contributes to bad practices and multiple transform fetching as a result, because domain code was accessing transform afterwards anyway, or even have direct transform references stored already. Its better to have specific logic outside pooling (e.g. on IGenericPoolElement) to modify transform in more performant way.

Best order of applying changes to transform now:

  • .Pool (this also calls .Commission to restore state)
  • T.SetX(...); // modify transform state
  • Perform T.Init() to run extra logic afterwards;