Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Which is best: one pool for all objects or a pool for each type?

Discussion in 'Scripting' started by winterfive, Aug 6, 2018.

  1. winterfive

    winterfive

    Joined:
    May 21, 2018
    Posts:
    31
    What is best practice and/or better for performance: one "large" pool for all your objects or a pool for each type? By type I refer to my initial enemy prefab (there's 30 of these at a time), future enemy prefabs, each enemy's explosion (particle system), etc. I am creating a VR game for GearVR (mobile) so I want to mind my memory usage/performance. Thanks!
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    The pool itself will just be a collection of refs. The memory usage is of the pool itself is small compared to the actual objects themselves. All the collection needs is the the Size * bytes per ref/pointer, which is about 8 or 12 bytes depending on the platform. 64-bit Win10 is 12 bytes per.

    So for example if you have a collection of 1000 scene objects that are X bytes in size each. Those are going to take up 1000X bytes of memory. But a single array of 1000 length should only be about 12000 bytes in size (~12KB). If you split them into 2 arrays of 500 each, it'll be 6000 bytes for each array. Or 12000 bytes.

    Where things will get slightly different is in the case of Lists/HashSets/etc.

    A List<T> is really just a wrapper around an array. And every so many adds it resizes that array to some nominal size larger. If you call TrimExcess it'll shrink that array to the current Count of the list effectively getting the same size as the array implementation plus a couple bytes for some extra data.

    Where as a HashSet<T> will always be some prime number in size larger than the actual Count. There is no 'TrimExcess' for HashSet, because it's counter to its implementation. This means that a single HashSet of 1000 objects technically takes up less space than 2 HashSets of 500 each.

    This same goes for Dictionary, since Dictionary is very similar to a HashSet in implementation.

    ...

    But again, with all that said, the memory is sort of small.

    If you had 10 HashSets with 1000 objects each, you'd probably have about 150KB of memory used. Where as 1 HashSet of 10,000 objects would be probably about 120KB. These are tiny amounts of memory in the grand scheme of things.

    A single 'texture' in your game can take up more space than all 10 of these pools combined.

    The real things taking up the space is the objects themselves. The Enemy instances, the Particle System instances, etc. Depending the object they can be tiny or huge. But the sort of collection they're in does not change their size.

    ...

    Where your choice in collection really should come down to is speed of access.

    For example... adding an entry to a List is faster than adding to a HashSet, because a List just inserts at the end. Where as a HashSet needs to calculate its hash and place it wherever. This hash calculation takes time.

    BUT, retrieving/removing/testing Contains on a HashSet is WAY faster than a List. Because a List has to loop over its entire contents (O(N)) to test where the object exists. Where as a HashSet just has to calculate the hash and look in the appropriate slot/bucket (O(1)).

    HashSets also act different than Lists, where a HashSet forces uniqueness. If you add the same object twice to the same set, that object only exists once in it. In the case of a Pool this can be useful, because if you accidentally double insert an object back into your Pool, there is the potential of handing out an instance already handed out.

    Queue and Stack also have their own benefits and limitations.

    Use the one that works for you, and gain your efficiency in that way.

    Because a Pool is going to do a lot of tests.

    Like say you have a single pool of every type. And you ask that Pool for a 'Bullet'. OK... if it's a List, how do you do this? Are you going to loop over EVERY entry in the pool and test if it's a bullet and return the first one you found?

    That's slow.

    Do you partition out the pool? How do you do this? Use multiple collections which is effectively multiple pools? Or do you just say "0-100 is X, 101-200 is Y, etc"? That makes insertion slow.

    ...

    TLDR;

    The memory usage of a single collection and multiple collections is so negligible in the grand scheme of things that you should focus more on the efficiency of insertion and retrieval!

    Pick the collection that works best for the job first and foremost.

    Then, and only then, if memory starts becoming a concern... do you profile and deal with those memory issues.

    But unless you're on some tiny embedded system with only megabytes worth of RAM available... well, that platform is going to have a lot more concerns than just the memory usage of a single collection vs multiple collections. I'd be more concerned with shrinking model/texture sizes, animation sizes, and other things that are way fatter.

    Consider this... a 512x512 image uncompressed is like having an array of ints that is 262,144 in length. That's ~1 million bytes in size. That same amount of space could hold a pool of 87,000 prefabs on a 64-bit OS (neglecting the size of the prefabs themselves).
     
  3. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Forget memory, pools don't really affect memory at all. What you are looking for in a pool is eliminating loading and unloading time.

    I would recommend an individual collection for each type of thing*. That will cut down on time searching through irrelevant objects each time you need something. Instead you can go straight to the correct collection.

    As to one pool or multiple pools, that's an implementation detail. If you are only pooling two or three different types, then give each type its own pool. If you are pooling hundreds of different types, then having a single pool where you specify the type you are interested in would be more convenient.

    * There are some interesting game design applications where you put multiple types into the same pool, then draw randomly. This tends to be more relevant for board game design then video game design.
     
  4. winterfive

    winterfive

    Joined:
    May 21, 2018
    Posts:
    31
     
  5. winterfive

    winterfive

    Joined:
    May 21, 2018
    Posts:
    31
    Ah, thank you! I believe there will only be five or so "types" of things so separate pools it is, for now.
     
  6. winterfive

    winterfive

    Joined:
    May 21, 2018
    Posts:
    31
    Thank you for all of this information and consideration! I really appreciate it.
     
  7. TheMannyzaur

    TheMannyzaur

    Joined:
    Sep 25, 2016
    Posts:
    2
    I logged in just to say thank you for this amazing piece of advice! This extends beyond Unity and object pooling in general and I've learned so much from this today
    Thank you again
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,514
    Kiwasi likes this.